[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6CBER1TQT7M41FMM40JR9C3":3,"skill-same:01M6CBER1TQT7M41FMM40JR9C3":44},{"skillNo":4,"name":5,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":17,"publishedAt":19,"linkOnly":20,"markdownContent":21,"markdownBody":21,"frontMatter":22,"refName":23,"commitSha":24,"skillDirectory":25,"forksCount":13,"repositoryHtmlUrl":26,"repositoryHomepageUrl":6,"ownerHtmlUrl":27,"ownerBlogUrl":6,"ownerDescription":28,"updatedAt":29,"install":30,"resources":43},"01M6CBER1TQT7M41FMM40JR9C3","13-frame",null,"devwinsoft\u002Fdevian","devwinsoft","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F22570160?v=4","01M6C6RR8W05NH8NPKQNCE3Q72","maoshy","01M6C6RRDJ5TJJCZXRTY801YDH",0,"MIT","https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian\u002Fblob\u002Fmain\u002Fskills\u002Fdevian-unity\u002F23-ui-package\u002F13-ui-toast-system\u002F13-frame\u002FSKILL.md",[],[18],"13 frame","2026-11-10 10:12:30",false,"# 13-frame — UIToastFrame\n\nStatus: ACTIVE\nAppliesTo: v11\n\n---\n\n## 목적\n\n개별 toast 한 장을 담당하는 pooled frame.\nmessage\u002Fcolor bind, show\u002Fhide tween, lifetime coroutine, non-blocking 강제를 수행한다.\n\n---\n\n## SSOT\n\n### Implementation Location (3-path mirror)\n\n| 경로 | 역할 |\n|------|------|\n| `framework-cs\u002Fupm\u002Fcom.devian.foundation\u002FSamples~\u002FUIPackage\u002FRuntime\u002FToast\u002FUIToastFrame.cs` | UPM mirror |\n| `framework-cs\u002Fapps\u002FUnityExample\u002FPackages\u002Fcom.devian.foundation\u002FSamples~\u002FUIPackage\u002FRuntime\u002FToast\u002FUIToastFrame.cs` | Packages mirror |\n| `framework-cs\u002Fapps\u002FUnityExample\u002FAssets\u002FSamples\u002FDevian Foundation\u002F0.1.0\u002FUIPackage\u002FRuntime\u002FToast\u002FUIToastFrame.cs` | 현재 workspace 구현 기준 |\n\n### Class Signature\n\n```csharp\nnamespace Devian\n{\n    [RequireComponent(typeof(UITransitionPlayer))]\n    public sealed class UIToastFrame : UIBaseFrame, IPoolable\n    {\n        \u002F\u002F ─── Inspector ───\n        [SerializeField] private TextMeshProUGUI _messageText;\n        [SerializeField] private UI_TRANSITION_PRESET_ID _showTransitionId;\n        [SerializeField] private UI_TRANSITION_PRESET_ID _hideTransitionId;\n        [SerializeField] private Graphic _backgroundGraphic;\n        [SerializeField] private CanvasGroup _canvasGroup;\n\n        \u002F\u002F Background\u002FText colors per ToastType (Info\u002FSuccess\u002FWarning\u002FError)\n\n        \u002F\u002F ─── State ───\n        public bool isHiding { get; }\n        private string _currentMessage;\n        private LayoutSnapshot _initialLayout;\n\n        \u002F\u002F ─── Pool ───\n        public void OnPoolSpawned();    \u002F\u002F base _HandlePoolSpawned bridge\n        public void OnPoolDespawned();  \u002F\u002F base _HandlePoolDespawned bridge\n\n        \u002F\u002F ─── Control ───\n        public void Bind(string message, ToastType toastType);\n        public bool HasMessage(string message);                     \u002F\u002F Ordinal 비교\n        public void Show(float duration, Action\u003CUIToastFrame> onHidden);\n        public void RefreshDuration(float duration);                \u002F\u002F isHiding이면 무시\n        internal void ApplyGroupOffset(Vector2 offset);            \u002F\u002F prefab 기본 RectTransform 복원 후 offset 적용\n    }\n}\n```\n\n---\n\n## Inspector 기본값 (ResolveDefaults)\n\n모든 `[SerializeField]`가 null이면 same-GO \u002F child fallback:\n\n| 필드 | fallback |\n|------|---------|\n| `_messageText` | `GetComponentInChildren\u003CTextMeshProUGUI>(true)` |\n| `_backgroundGraphic` | `GetComponent\u003CImage>()` → `GetComponentInChildren\u003CImage>(true)` |\n| `_canvasGroup` | `GetComponentInChildren\u003CCanvasGroup>(true)` |\n\n`UITransitionPlayer`는 `[RequireComponent(typeof(UITransitionPlayer))]`로 같은 GameObject에 필수다.\n\n---\n\n## Non-Blocking (ApplyNonBlocking)\n\n- `_canvasGroup.interactable = false`\n- `_canvasGroup.blocksRaycasts = false`\n- `GetComponentsInChildren\u003CGraphic>(true)` 전체에 `raycastTarget = false`\n\nspawn 시(`OnPoolSpawned`) 및 bind 시(`Bind`) 두 번 호출된다.\n\n## Pool Contract\n\n- public `OnPoolSpawned()` \u002F `OnPoolDespawned()`는 각각 base `_HandlePoolSpawned()` \u002F `_HandlePoolDespawned()` bridge다.\n- `onPoolSpawned()`는 `ResolveDefaults()` + `CancelInternal()` + `ApplyNonBlocking()`를 수행한다.\n- `onPoolDespawned()`는 `CancelInternal()` 후 `_messageText.text`와 `_currentMessage`를 비운다.\n- base `UIBaseFrame`가 despawn 시 init state를 reset하므로, 다음 respawn 뒤 group이 `_Init()` \u002F `_InitComplete()`를 다시 호출하면 `onInit()`이 재실행된다.\n\n## Bind 규약\n\n- `_currentMessage = request.Message`\n- `_messageText.text = _currentMessage`\n- `_messageText.ForceMeshUpdate()` 호출\n- `_messageText == null`이면 warning 로그 출력\n- `HasMessage()`는 TMP text가 아니라 `_currentMessage`를 기준으로 비교한다\n\n## Layout 규약\n\n- `onAwake()` \u002F `onInit()`에서 prefab 원본 `RectTransform` 값을 snapshot으로 캐시한다.\n- `ApplyGroupOffset(offset)`는 anchor\u002Fpivot\u002FsizeDelta\u002FlocalScale\u002FanchoredPosition을 원본 값으로 복원한 뒤 offset만 더한다.\n- `ApplyGroupOffset(offset)` 직후 `UITransitionPlayer.RefreshBaseline()`을 호출해 show\u002Fhide move tween의 기준점을 현재 슬롯 좌표로 동기화한다.\n- 따라서 group은 frame 기본 크기를 덮어쓰지 않는다.\n\n---\n\n## Show \u002F Lifetime 흐름\n\n```\nShow(duration, onHidden)\n  → CancelInternal() (이전 코루틴\u002Ftween 정리)\n  → isHiding = false\n  → same-GO UITransitionPlayer + _showTransitionId 기반 preset 재생\n  → RestartLifetime(duration)\n      → CoLifetime(duration) 코루틴 시작\n      → WaitForSecondsRealtime(duration)\n      → HideInternal()\n          → isHiding = true\n          → same-GO UITransitionPlayer + _hideTransitionId 기반 preset 재생\n          → handle null 또는 IsCanceled → NotifyHidden() 즉시 호출\n```\n\n---\n\n## Hide tween 완료 → Despawn\n\n- `NotifyHidden()` → `_onHidden?.Invoke(this)` → `UIToastGroup.OnFrameHidden(frame)`\n- `OnFrameHidden`에서 `BundlePool.Despawn(frame)` 호출\n- slot release는 **lifetime 만료가 아니라 hide tween 완료** 기준\n\n---\n\n## Color 테이블 (default)\n\n| ToastType | Background | Text |\n|-----------|-----------|------|\n| Info | `(0.15, 0.15, 0.15, 0.95)` | White |\n| Success | `(0.16, 0.45, 0.24, 0.95)` | White |\n| Warning | `(0.75, 0.50, 0.08, 0.95)` | White |\n| Error | `(0.70, 0.18, 0.18, 0.95)` | White |\n\nInspector에서 색상 override 가능.\n\n---\n\n## Reference\n\n- Overview: `..\u002F00-overview\u002FSKILL.md`\n- Group: `..\u002F12-group\u002FSKILL.md`\n- Frame ID: `..\u002F15-ui-toast-frame-id\u002FSKILL.md`\n- UITransitionPlayer: `..\u002F..\u002F22-ui-tween-system\u002F14-ui-transition-player\u002FSKILL.md`\n",{},"main","8cb5bd877a3a7004867d2f55697cb9e4ff2f1734","skills\u002Fdevian-unity\u002F23-ui-package\u002F13-ui-toast-system\u002F13-frame","https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian","https:\u002F\u002Fgithub.com\u002Fdevwinsoft","Kim Hyong Joon","2026-04-12 18:44:10",{"source":7,"skillName":5,"commands":31,"prompt":41,"zipAvailable":42},[32,35,38],{"tool":33,"command":34},"npx","npx skills add devwinsoft\u002Fdevian --skill 13-frame",{"tool":36,"command":37},"bunx","bunx skills add devwinsoft\u002Fdevian --skill 13-frame",{"tool":39,"command":40},"pnpm","pnpm dlx skills add devwinsoft\u002Fdevian --skill 13-frame","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian\u002Fblob\u002Fmain\u002Fskills\u002Fdevian-unity\u002F23-ui-package\u002F13-ui-toast-system\u002F13-frame\u002FSKILL.md",true,[],[45,51,59,67,76,83],{"skillNo":46,"name":47,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":48,"categories":49,"keywords":50,"publishedAt":19},"01M6CBX3K2JJV38ANHVGCAT8A0","devian","https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian\u002Fblob\u002Fmain\u002Fskills\u002Fdevian\u002FSKILL.md",[],[47],{"skillNo":52,"name":53,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":54,"categories":55,"keywords":56,"publishedAt":19},"01M6CBX210BFMM8AMN770J9WY8","90-project-archive","https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian\u002Fblob\u002Fmain\u002Fskills\u002Fdevian\u002F80-tools\u002F90-project-archive\u002FSKILL.md",[],[57,58],"archive","90 project archive",{"skillNo":60,"name":61,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":62,"categories":63,"keywords":64,"publishedAt":19},"01M6CBWZSGRR9R3YJZB3177667","30-env-management","https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian\u002Fblob\u002Fmain\u002Fskills\u002Fdevian\u002F80-tools\u002F30-env-management\u002FSKILL.md",[],[65,66],"management","30 env management",{"skillNo":68,"name":69,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":70,"categories":71,"keywords":72,"publishedAt":19},"01M6CBWYD3TGWBQV7V35551THF","19-page-push-send","https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian\u002Fblob\u002Fmain\u002Fskills\u002Fdevian\u002F80-tools\u002F20-operation\u002F19-page-push-send\u002FSKILL.md",[],[73,74,75],"push","page","19 page push send",{"skillNo":77,"name":78,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":79,"categories":80,"keywords":81,"publishedAt":19},"01M6CBWWRQ05A7604GHJKTPJTF","18-page-savedata","https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian\u002Fblob\u002Fmain\u002Fskills\u002Fdevian\u002F80-tools\u002F20-operation\u002F18-page-savedata\u002FSKILL.md",[],[74,82],"18 page savedata",{"skillNo":84,"name":85,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":86,"categories":87,"keywords":88,"publishedAt":19},"01M6CBWVH4S2VF1V7B2GBTESCT","17-page-deobfuscate","https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian\u002Fblob\u002Fmain\u002Fskills\u002Fdevian\u002F80-tools\u002F20-operation\u002F17-page-deobfuscate\u002FSKILL.md",[],[74,89],"17 page deobfuscate"]