Files
HeuJung 8cf829f953 fix(core,extensions): 설치 완료 스크롤 + 미인증 화면 테마 버튼 무반응 수정
Modern PHP User Group 2026-09 정기모임 설치 리뷰에서 접수된 제보 2건.

설치 완료·실패·중단·필수파일 안내는 페이지 최상단에 뜨는데, 진행 로그를 보느라
화면이 아래에 머물러 있으면 안내가 눈에 들어오지 않았다. 네 경로 모두 DOM 변경이
끝난 뒤 결과 섹션으로 부드럽게 이동시킨다(모션 최소화 설정 시 즉시 이동).

미인증 3화면의 테마 버튼은 setTheme 을 params.theme 으로 불렀는데 핸들러는
action.target 만 읽는다. 엔진에 상호 폴백이 없어 클릭이 콘솔 경고 한 줄만 남기고
아무 일도 하지 않았다. 로그인 이후 화면은 핸들러를 거치지 않는 ThemeToggle 을
쓰기 때문에 정상이었고, 그래서 이 세 화면에서만 나타났다.

같은 계약 불일치가 편집기 액션 레시피(admin 9·basic 3)와 두 템플릿 문서에도 있어
함께 고쳤다. 편집기로 만든 액션은 생성 즉시 no-op 이 되는데 오류가 남지 않는다.
정적 검사 레지스트리가 오히려 틀린 계약(params.theme 필수)을 강제하고 있어 올바른
형태를 막고 있었으므로 두 미러를 함께 정정했다.

테마를 고치는 과정에서 별개 결함이 드러났다. GDPR 스토리지 인터셉터가 기능 쿠키
미동의 상태에서 필수 목록 밖 저장을 부팅마다 파기하는데, 화면 테마가 그 목록에서
빠져 있었다. 저장소의 setItem 호출을 기계 도출해 대조한 결과 같은 이유로 사라지던
항목이 13건 더 있었다 — 비회원 주문 조회, 결제창 복귀 기록, 본인인증 복귀 기록,
관리자 화면 상태, 자산 주소 형식 캐시. 전량 필수로 분류하고, 동의 안내 문구가
"다크모드는 기능 쿠키" 라고 말하던 부분을 사실에 맞게 정정했다(기설치본의 저장된
문구는 업그레이드 스텝이 정정하며, 운영자가 고친 문구는 건드리지 않는다).

재발 방지는 허용목록을 직접 import 하고 모집단을 디렉토리 순회로 도출하는 커버리지
테스트가 맡는다 — 손으로 열거하지 않으므로 새 저장 키가 등재를 빠뜨리면 붉어진다.
2026-09-03 18:42:30 +09:00

337 lines
8.8 KiB
JSON

{
"comment": "친화 명칭 → 핸들러 JSON. params 친화 입력 + build 틀. 사용자 화면 대표 동작.",
"goToPage": {
"label": "$t:editor.action.go_to_page.label",
"params": [
{
"key": "url",
"label": "$t:editor.action.go_to_page.param_url",
"widget": "page-picker"
}
],
"build": {
"handler": "navigate",
"params": {
"path": "{{url}}"
}
}
},
"showMessage": {
"label": "$t:editor.action.show_message.label",
"params": [
{
"key": "text",
"label": "$t:editor.action.show_message.param_text",
"widget": "i18n-text"
},
{
"key": "tone",
"label": "$t:editor.action.show_message.param_tone",
"widget": "select",
"options": [
{
"value": "success",
"label": "$t:editor.action.show_message.tone_success"
},
{
"value": "error",
"label": "$t:editor.action.show_message.tone_error"
},
{
"value": "info",
"label": "$t:editor.action.show_message.tone_info"
}
]
}
],
"build": {
"handler": "toast",
"params": {
"message": "{{text}}",
"type": "{{tone}}"
}
}
},
"changeState": {
"label": "$t:editor.action.change_state.label",
"params": [
{
"key": "key",
"label": "$t:editor.action.change_state.param_key",
"widget": "state-key-picker"
},
{
"key": "value",
"label": "$t:editor.action.change_state.param_value",
"widget": "text"
}
],
"build": {
"handler": "setState",
"params": {
"target": "local",
"{{key}}": "{{value}}"
}
}
},
"refreshData": {
"label": "$t:editor.action.refresh_data.label",
"params": [
{
"key": "src",
"label": "$t:editor.action.refresh_data.param_src",
"widget": "datasource-picker"
}
],
"build": {
"handler": "refetchDataSource",
"params": {
"dataSourceId": "{{src}}"
}
}
},
"callServerThen": {
"label": "$t:editor.action.call_server_then.label",
"params": [
{
"key": "endpoint",
"label": "$t:editor.action.call_server_then.param_endpoint",
"widget": "text"
},
{
"key": "onSuccess",
"label": "$t:editor.action.call_server_then.param_on_success",
"widget": "action-list"
},
{
"key": "onError",
"label": "$t:editor.action.call_server_then.param_on_error",
"widget": "action-list"
}
],
"build": {
"handler": "apiCall",
"target": "{{endpoint}}",
"onSuccess": "{{onSuccess}}",
"onError": "{{onError}}"
}
},
"setTheme": {
"label": "$t:editor.action.set_theme.label",
"params": [
{
"key": "target",
"label": "$t:editor.action.set_theme.param_target",
"widget": "select",
"options": [
{ "value": "auto", "label": "$t:editor.action.set_theme.target_auto" },
{ "value": "light", "label": "$t:editor.action.set_theme.target_light" },
{ "value": "dark", "label": "$t:editor.action.set_theme.target_dark" }
]
}
],
"build": {
"handler": "setTheme",
"target": "{{target}}"
}
},
"savePreferredCurrency": {
"label": "$t:editor.action.save_preferred_currency.label",
"params": [
{
"key": "currency",
"label": "$t:editor.action.save_preferred_currency.param_currency",
"widget": "text"
}
],
"build": {
"handler": "savePreferredCurrency",
"params": { "currency": "{{currency}}" }
}
},
"setCartOption": {
"label": "$t:editor.action.set_cart_option.label",
"params": [
{
"key": "itemId",
"label": "$t:editor.action.set_cart_option.param_item_id",
"widget": "text"
},
{
"key": "optionKey",
"label": "$t:editor.action.set_cart_option.param_option_key",
"widget": "text"
},
{
"key": "optionValue",
"label": "$t:editor.action.set_cart_option.param_option_value",
"widget": "text"
}
],
"build": {
"handler": "setCartOption",
"params": {
"itemId": "{{itemId}}",
"optionKey": "{{optionKey}}",
"optionValue": "{{optionValue}}"
}
}
},
"openCartDeleteModal": {
"label": "$t:editor.action.open_cart_delete_modal.label",
"params": [
{
"key": "itemId",
"label": "$t:editor.action.open_cart_delete_modal.param_item_id",
"widget": "text"
}
],
"build": {
"handler": "openCartDeleteModal",
"params": { "itemId": "{{itemId}}" }
}
},
"openCartOptionModal": {
"label": "$t:editor.action.open_cart_option_modal.label",
"params": [
{
"key": "itemId",
"label": "$t:editor.action.open_cart_option_modal.param_item_id",
"widget": "text"
}
],
"build": {
"handler": "openCartOptionModal",
"params": { "itemId": "{{itemId}}" }
}
},
"recalculateCart": {
"label": "$t:editor.action.recalculate_cart.label",
"params": [],
"build": { "handler": "recalculateCart" }
},
"getDisplayPrice": {
"label": "$t:editor.action.get_display_price.label",
"params": [
{
"key": "currency",
"label": "$t:editor.action.get_display_price.param_currency",
"widget": "text"
}
],
"build": {
"handler": "getDisplayPrice",
"params": { "currency": "{{currency}}" }
}
},
"initTheme": {
"label": "$t:editor.action.init_theme.label",
"params": [
{
"key": "target",
"label": "$t:editor.action.init_theme.param_target",
"widget": "select",
"options": [
{ "value": "auto", "label": "$t:editor.action.init_theme.target_auto" },
{ "value": "light", "label": "$t:editor.action.init_theme.target_light" },
{ "value": "dark", "label": "$t:editor.action.init_theme.target_dark" }
]
}
],
"build": {
"handler": "initTheme",
"target": "{{target}}"
}
},
"initCartKey": {
"label": "$t:editor.action.init_cart_key.label",
"params": [],
"build": { "handler": "initCartKey" }
},
"loadPreferredCurrency": {
"label": "$t:editor.action.load_preferred_currency.label",
"params": [],
"build": { "handler": "loadPreferredCurrency" }
},
"getCartKey": {
"label": "$t:editor.action.get_cart_key.label",
"params": [],
"build": { "handler": "getCartKey" }
},
"clearCartKey": {
"label": "$t:editor.action.clear_cart_key.label",
"params": [],
"build": { "handler": "clearCartKey" }
},
"saveToStorage": {
"label": "$t:editor.action.save_to_storage.label",
"params": [
{
"key": "key",
"label": "$t:editor.action.save_to_storage.param_key",
"widget": "text"
},
{
"key": "value",
"label": "$t:editor.action.save_to_storage.param_value",
"widget": "i18n-text"
}
],
"build": {
"handler": "saveToStorage",
"params": { "key": "{{key}}", "value": "{{value}}" }
}
},
"loadFromStorage": {
"label": "$t:editor.action.load_from_storage.label",
"params": [
{
"key": "key",
"label": "$t:editor.action.load_from_storage.param_key",
"widget": "text"
},
{
"key": "default",
"label": "$t:editor.action.load_from_storage.param_default",
"widget": "text"
}
],
"build": {
"handler": "loadFromStorage",
"params": { "key": "{{key}}", "default": "{{default}}" }
}
},
"saveGuestOrderToken": {
"comment": "비회원 주문 토큰 저장 — sirsoft-basic 템플릿 소유 핸들러(storageHandlers). verify 응답의 토큰/주문번호/만료시각을 sessionStorage + _global.guestOrderToken 에 저장. 세 입력 모두 응답값(데이터 칩).",
"label": "$t:editor.action.save_guest_order_token.label",
"params": [
{
"key": "token",
"label": "$t:editor.action.save_guest_order_token.param_token",
"widget": "data-chip",
"required": true
},
{
"key": "orderNumber",
"label": "$t:editor.action.save_guest_order_token.param_order_number",
"widget": "data-chip",
"required": true
},
{
"key": "expiresAt",
"label": "$t:editor.action.save_guest_order_token.param_expires_at",
"widget": "data-chip",
"required": true
}
],
"build": {
"handler": "saveGuestOrderToken",
"params": {
"token": "{{token}}",
"orderNumber": "{{orderNumber}}",
"expiresAt": "{{expiresAt}}"
}
}
}
}