@siteping/widget 0.9.10 → 0.9.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/dom-utils.ts","../src/icons.ts","../src/styles/theme.ts","../../core/src/errors.ts","../../core/src/type-utils.ts","../../core/src/types.ts","../src/i18n/en.ts","../src/i18n/index.ts","../src/export-utils.ts","../src/panel-bulk.ts","../src/panel-detail.ts","../src/panel-sort.ts","../src/panel-stats.ts","../src/shortcuts.ts"],"names":["parseSvg","svgString","svg","attr","el","tag","attrs","element","key","value","setText","text","setButtonLoading","btn","snapshot","formatRelativeDate","isoString","locale","diff","seconds","rtf","minutes","hours","days","ICON_SITEPING","ICON_CHAT","ICON_ANNOTATE","ICON_EYE","ICON_EYE_OFF","ICON_CLOSE","ICON_SEARCH","ICON_CHECK","ICON_QUESTION","ICON_CHANGE","ICON_BUG","ICON_OTHER","ICON_LAYERS","ICON_DOT_OPEN","ICON_CHEVRON_DOWN","ICON_UNDO","ICON_TRASH","DEFAULT_ACCENT","HEX6_RE","HEX3_RE","HEX8_RE","normalizeHex","raw","short","darkenHex","hex","amount","r","g","b","prefersDark","resolveTheme","theme","buildThemeColors","accent","dark","getTypeColor","type","colors","getTypeBgColor","cssVariables","SitepingError","message","code","retryable","SitepingNetworkError","SitepingValidationError","SitepingAuthError","isRecord","hasOwn","BUILTIN_LOCALES","flattenAnnotation","ann","en","LOCALES","BUILTIN_NON_EN","l","isBuiltinNonEn","lang","normaliseLang","loadLocale","cached","mod","dict","createT","getTypeLabel","t","ICON_EXPORT","ICON_CSV","ICON_JSON","EXPORT_CSS","CSV_COLUMNS","escapeCsvField","feedbacksToCsv","feedbacks","header","rows","fb","col","feedbacksToJson","downloadFile","content","filename","mimeType","blob","url","anchor","ExportButton","_colors","getFeedbacks","label","e","csvOption","jsonOption","iconSvg","labelText","onClick","option","iconWrap","labelEl","format","projectName","date","safeName","ICON_CHECKBOX","ICON_CHECKBOX_CHECKED","BULK_CSS","BulkActions","callbacks","actions","deselectBtn","feedbackId","wrapper","feedbackIds","checkbox","container","id","prevSelected","count","visible","resolve","del","resolveLabel","deleteLabel","isChecked","allSelected","escapedId","card","ids","restoreResolve","restoreDelete","ICON_ARROW_LEFT","ICON_MAP_PIN","ICON_LINK","ICON_USER","ICON_CALENDAR","ICON_MONITOR","ICON_CODE","ICON_CROSSHAIR","ICON_CHEVRON","ICON_TERMINAL","DETAIL_CSS","parseBrowser","ua","m","formatFullDate","extractPathname","isSafeImageUrl","truncate","str","max","hasDiagnostics","diagnostics","consoleLen","networkLen","formatDuration","ms","DetailView","backBtn","feedback","number","title","badge","sectionIndex","statusSection","messageSection","messageSectionTitle","messageBlock","screenshotSection","screenshotSectionTitle","img","metaSection","metaSectionTitle","annSection","annSectionTitle","annTitleText","diagSection","diagSectionTitle","diagTitleText","index","section","isResolved","sectionTitle","statusRow","pill","dot","pillLabel","span","deleteSpan","meta","pathname","name","email","resolvedDate","buildValue","row","info","tagDisplay","gotoBtn","gotoLabel","diag","consoleEntries","networkEntries","errorCount","toggle","toggleLabel","leftRow","counts","consoleCount","networkCount","body","group","list","entry","item","level","msg","status","method","next","ICON_SORT","ICON_PAGE","TYPE_ORDER","sortFeedbacks","mode","sorted","a","typeA","typeB","statusA","statusB","truncatePath","path","maxLength","ellipsis","keep","groupFeedbacksByPage","groups","existing","createPageGroupHeader","pagePath","chevronWrap","pageIcon","pathEl","displayPath","countEl","isExpanded","PanelSortControls","onChange","sortIcon","sortLabel","groupIcon","groupLabel","options","opt","labelMap","SORT_CSS","STATS_CSS","PanelStats","itemOpen","dotOpen","labelOpen","itemResolved","dotResolved","labelResolved","itemBugs","dotBugs","labelBugs","progress","track","total","openCount","resolvedCount","bugCount","pct","progressText","ICON_KEYBOARD","getFocusedCardIndex","listContainer","cards","i","focusCardByIndex","clamped","target","SHORTCUT_DEFS","SHORTCUTS_CSS","ICON_CLOSE_SM","KeyboardShortcuts","root","active","handler","overlay","titleText","closeBtn","grid","def","keysWrap","sep","kbd","desc"],"mappings":"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AA,CAMxBC,EAAAA,CAAW;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAA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AA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA,EA+sBpC,SAASC,GAAaC,CAAAA,CAAoB,CAExC,GAAI,QAAA,CAAS,IAAA,CAAKA,CAAE,CAAA,CAAG,CACrB,IAAMC,EAAID,CAAAA,CAAG,KAAA,CAAM,eAAe,CAAA,CAClC,OAAOC,CAAAA,CAAI,QAAQA,CAAAA,CAAE,CAAC,CAAC,CAAA,CAAA,CAAK,MAC9B,CACA,GAAI,QAAA,CAAS,IAAA,CAAKD,CAAE,CAAA,EAAK,QAAA,CAAS,KAAKA,CAAE,CAAA,CAAG,CAC1C,IAAMC,CAAAA,CAAID,CAAAA,CAAG,MAAM,eAAe,CAAA,CAClC,OAAOC,CAAAA,CAAI,CAAA,MAAA,EAASA,CAAAA,CAAE,CAAC,CAAC,CAAA,CAAA,CAAK,OAC/B,CACA,GAAI,YAAA,CAAa,KAAKD,CAAE,CAAA,CAAG,CACzB,IAAMC,CAAAA,CAAID,EAAG,KAAA,CAAM,mBAAmB,CAAA,CACtC,OAAOC,CAAAA,CAAI,CAAA,QAAA,EAAWA,EAAE,CAAC,CAAC,CAAA,CAAA,CAAK,SACjC,CACA,GAAI,YAAY,IAAA,CAAKD,CAAE,CAAA,EAAK,CAAC,WAAA,CAAY,IAAA,CAAKA,CAAE,CAAA,CAAG,CACjD,IAAMC,CAAAA,CAAID,CAAAA,CAAG,MAAM,kBAAkB,CAAA,CACrC,OAAOC,CAAAA,CAAI,CAAA,OAAA,EAAUA,CAAAA,CAAE,CAAC,CAAC,CAAA,CAAA,CAAK,QAChC,CACA,GAAI,WAAA,CAAY,KAAKD,CAAE,CAAA,EAAK,CAAC,SAAA,CAAU,IAAA,CAAKA,CAAE,EAAG,CAC/C,IAAMC,EAAID,CAAAA,CAAG,KAAA,CAAM,mBAAmB,CAAA,CACtC,OAAOC,CAAAA,CAAI,CAAA,OAAA,EAAUA,CAAAA,CAAE,CAAC,CAAC,CAAA,CAAA,CAAK,QAChC,CACA,OAAO,SACT,CAGA,SAASC,CAAAA,CAAezJ,CAAAA,CAAmBC,CAAAA,CAAwB,CACjE,GAAI,CAEF,OADU,IAAI,IAAA,CAAKD,CAAS,CAAA,CACnB,cAAA,CAAeC,EAAQ,CAC9B,IAAA,CAAM,SAAA,CACN,KAAA,CAAO,MAAA,CACP,GAAA,CAAK,UACL,IAAA,CAAM,SAAA,CACN,MAAA,CAAQ,SACV,CAAC,CACH,MAAQ,CACN,OAAOD,CACT,CACF,CAGA,SAAS0J,GAAgB7D,CAAAA,CAAqB,CAC5C,GAAI,CACF,OAAO,IAAI,GAAA,CAAIA,CAAG,CAAA,CAAE,QACtB,CAAA,KAAQ,CACN,OAAOA,CACT,CACF,CAUA,SAAS8D,EAAAA,CAAe9D,CAAAA,CAAsB,CAQ5C,OAJI,CAAA,EAAA,gCAAA,CAAiC,IAAA,CAAKA,CAAG,CAAA,EAIzC,cAAA,CAAe,KAAKA,CAAG,CAAA,CAE7B,CAGA,SAAS+D,CAAAA,CAASC,EAAaC,CAAAA,CAAqB,CAClD,OAAID,CAAAA,CAAI,MAAA,EAAUC,CAAAA,CAAYD,EACvBA,CAAAA,CAAI,KAAA,CAAM,CAAA,CAAGC,CAAAA,CAAM,CAAC,CAAA,CAAI,QACjC,CAOA,SAASC,EAAAA,CAAeC,CAAAA,CAAuD,CAC7E,GAAI,CAACA,CAAAA,CAAa,OAAO,OACzB,IAAMC,CAAAA,CAAa,MAAM,OAAA,CAAQD,CAAAA,CAAY,OAAO,CAAA,CAAIA,CAAAA,CAAY,OAAA,CAAQ,OAAS,CAAA,CAC/EE,CAAAA,CAAa,KAAA,CAAM,OAAA,CAAQF,CAAAA,CAAY,OAAO,EAAIA,CAAAA,CAAY,OAAA,CAAQ,MAAA,CAAS,CAAA,CACrF,OAAOC,CAAAA,CAAa,GAAKC,CAAAA,CAAa,CACxC,CAGA,SAASC,EAAAA,CAAeC,EAAoB,CAC1C,OAAI,CAAC,MAAA,CAAO,QAAA,CAASA,CAAE,GAAKA,CAAAA,CAAK,CAAA,CAAU,QAAA,CACvCA,CAAAA,CAAK,GAAA,CAAa,CAAA,EAAG,KAAK,KAAA,CAAMA,CAAE,CAAC,CAAA,GAAA,CAAA,CAChC,CAAA,EAAA,CAAIA,CAAAA,CAAK,KAAM,OAAA,CAAQ,CAAC,CAAC,CAAA,EAAA,CAClC,KAiBaC,CAAAA,CAAN,KAAiB,CAYtB,WAAA,CACmBvH,CAAAA,CACAsE,CAAAA,CACjB1C,EACAzE,CAAAA,CACA,CAJiB,IAAA,CAAA,MAAA,CAAA6C,CAAAA,CACA,IAAA,CAAA,SAAA,CAAAsE,CAAAA,CAIjB,KAAK,CAAA,CAAI1C,CAAAA,CACT,IAAA,CAAK,MAAA,CAASzE,CAAAA,CAGd,IAAA,CAAK,QAAUb,CAAAA,CAAG,KAAA,CAAO,CAAE,KAAA,CAAO,WAAY,CAAC,CAAA,CAC/C,IAAA,CAAK,OAAA,CAAQ,YAAA,CAAa,MAAA,CAAQ,QAAQ,EAC1C,IAAA,CAAK,OAAA,CAAQ,YAAA,CAAa,YAAA,CAAc,iBAAiB,CAAA,CACzD,KAAK,OAAA,CAAQ,YAAA,CAAa,aAAA,CAAe,MAAM,CAAA,CAG/C,IAAM+F,EAAS/F,CAAAA,CAAG,KAAA,CAAO,CAAE,KAAA,CAAO,kBAAmB,CAAC,CAAA,CAEhDkL,CAAAA,CAAU,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA,CAC/CA,EAAQ,IAAA,CAAO,QAAA,CACfA,CAAAA,CAAQ,SAAA,CAAY,gBAAA,CACpBA,CAAAA,CAAQ,aAAa,YAAA,CAAc,IAAA,CAAK,CAAA,CAAE,aAAa,CAAC,CAAA,CACxDA,EAAQ,WAAA,CAAYtL,CAAAA,CAAS2J,EAAe,CAAC,CAAA,CAC7C2B,EAAQ,gBAAA,CAAiB,OAAA,CAAS,IAAM,CACtC,IAAA,CAAK,IAAA,GACL,IAAA,CAAK,SAAA,CAAU,MAAA,GACjB,CAAC,CAAA,CAED,KAAK,OAAA,CAAQ,WAAA,CAAYnF,CAAM,CAAA,CAC/BA,CAAAA,CAAO,WAAA,CAAYmF,CAAO,CAAA,CAK1B,IAAA,CAAK,QAAUlL,CAAAA,CAAG,KAAA,CAAO,CAAE,KAAA,CAAO,mBAAoB,CAAC,CAAA,CACvD,IAAA,CAAK,OAAA,CAAQ,YAAY,IAAA,CAAK,OAAO,EACvC,CAnCmB,MAAA,CACA,SAAA,CAbV,QAED,UAAA,CAAa,KAAA,CACb,eAAA,CAA2C,IAAA,CAClC,OAAA,CACA,CAAA,CACA,OACT,UAAA,CAAuC,IAAA,CACvC,SAAA,CAAsC,IAAA,CACtC,YAAA,CAAe,KAAA,CAyCvB,KAAKmL,CAAAA,CAA4BC,CAAAA,CAAsB,CACrD,IAAA,CAAK,eAAA,CAAkBD,CAAAA,CACvB,KAAK,YAAA,CAAe,KAAA,CAGpB,IAAMpF,CAAAA,CAAS,IAAA,CAAK,OAAA,CAAQ,cAA2B,mBAAmB,CAAA,CAC1E,GAAI,CAACA,CAAAA,CAAQ,OAEb,IAAMmF,CAAAA,CAAUnF,CAAAA,CAAO,cAA2B,iBAAiB,CAAA,CACnE,GAAI,CAACmF,CAAAA,CAAS,OACdnF,CAAAA,CAAO,eAAA,CAAgBmF,CAAO,EAE9B,IAAMG,CAAAA,CAAQrL,CAAAA,CAAG,MAAA,CAAQ,CAAE,KAAA,CAAO,iBAAkB,CAAC,CAAA,CACrDM,CAAAA,CAAQ+K,CAAAA,CAAO,IAAA,CAAK,CAAA,CAAE,cAAc,CAAA,CAAE,OAAA,CAAQ,WAAY,MAAA,CAAOD,CAAM,CAAC,CAAC,CAAA,CACzErF,CAAAA,CAAO,WAAA,CAAYsF,CAAK,CAAA,CAExB,IAAMC,CAAAA,CAAQtL,CAAAA,CAAG,MAAA,CAAQ,CAAE,KAAA,CAAO,UAAW,CAAC,CAAA,CAC9CsL,CAAAA,CAAM,KAAA,CAAM,UAAA,CAAa3H,CAAAA,CAAewH,CAAAA,CAAS,KAAM,IAAA,CAAK,MAAM,EAClEG,CAAAA,CAAM,KAAA,CAAM,MAAQ9H,CAAAA,CAAa2H,CAAAA,CAAS,IAAA,CAAM,IAAA,CAAK,MAAM,CAAA,CAC3D7K,EAAQgL,CAAAA,CAAOH,CAAAA,CAAS,IAAI,CAAA,CAC5BpF,CAAAA,CAAO,WAAA,CAAYuF,CAAK,CAAA,CAGxB,IAAA,CAAK,OAAA,CAAQ,eAAA,EAAgB,CAE7B,IAAIC,EAAe,CAAA,CAGbC,CAAAA,CAAgB,KAAK,YAAA,CAAaD,CAAAA,EAAc,EACtD,IAAA,CAAK,kBAAA,CAAmBC,CAAAA,CAAeL,CAAQ,CAAA,CAC/C,IAAA,CAAK,QAAQ,WAAA,CAAYK,CAAa,CAAA,CAGtC,IAAMC,CAAAA,CAAiB,IAAA,CAAK,aAAaF,CAAAA,EAAc,CAAA,CACjDG,CAAAA,CAAsB1L,CAAAA,CAAG,KAAA,CAAO,CAAE,MAAO,yBAA0B,CAAC,EAC1EM,CAAAA,CAAQoL,CAAAA,CAAqB,KAAK,CAAA,CAAE,gBAAgB,CAAC,CAAA,CACrDD,CAAAA,CAAe,WAAA,CAAYC,CAAmB,CAAA,CAE9C,IAAMC,CAAAA,CAAe3L,CAAAA,CAAG,KAAA,CAAO,CAAE,MAAO,mBAAoB,CAAC,CAAA,CAO7D,GANA2L,CAAAA,CAAa,KAAA,CAAM,gBAAkBnI,CAAAA,CAAa2H,CAAAA,CAAS,KAAM,IAAA,CAAK,MAAM,EAC5E7K,CAAAA,CAAQqL,CAAAA,CAAcR,CAAAA,CAAS,OAAO,CAAA,CACtCM,CAAAA,CAAe,YAAYE,CAAY,CAAA,CACvC,IAAA,CAAK,OAAA,CAAQ,WAAA,CAAYF,CAAc,EAGnCN,CAAAA,CAAS,aAAA,EAAiBZ,EAAAA,CAAeY,CAAAA,CAAS,aAAa,CAAA,CAAG,CACpE,IAAMS,CAAAA,CAAoB,KAAK,YAAA,CAAaL,CAAAA,EAAc,EACpDM,CAAAA,CAAyB7L,CAAAA,CAAG,KAAA,CAAO,CAAE,KAAA,CAAO,yBAA0B,CAAC,CAAA,CAC7EM,CAAAA,CAAQuL,CAAAA,CAAwB,IAAA,CAAK,CAAA,CAAE,mBAAmB,CAAC,CAAA,CAC3DD,CAAAA,CAAkB,WAAA,CAAYC,CAAsB,CAAA,CAEpD,IAAMC,EAAM,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA,CACxCA,CAAAA,CAAI,UAAY,sBAAA,CAChBA,CAAAA,CAAI,GAAA,CAAMX,CAAAA,CAAS,aAAA,CACnBW,CAAAA,CAAI,IAAM,IAAA,CAAK,CAAA,CAAE,sBAAsB,CAAA,CACvCA,CAAAA,CAAI,OAAA,CAAU,OAIdA,CAAAA,CAAI,cAAA,CAAiB,aAAA,CACrBF,CAAAA,CAAkB,WAAA,CAAYE,CAAG,EACjC,IAAA,CAAK,OAAA,CAAQ,YAAYF,CAAiB,EAC5C,CAGA,IAAMG,CAAAA,CAAc,IAAA,CAAK,YAAA,CAAaR,CAAAA,EAAc,CAAA,CAC9CS,EAAmBhM,CAAAA,CAAG,KAAA,CAAO,CAAE,KAAA,CAAO,yBAA0B,CAAC,EAOvE,GANAM,CAAAA,CAAQ0L,CAAAA,CAAkB,IAAA,CAAK,CAAA,CAAE,iBAAiB,CAAC,CAAA,CACnDD,CAAAA,CAAY,YAAYC,CAAgB,CAAA,CACxC,KAAK,aAAA,CAAcD,CAAAA,CAAaZ,CAAQ,CAAA,CACxC,IAAA,CAAK,OAAA,CAAQ,YAAYY,CAAW,CAAA,CAGhCZ,CAAAA,CAAS,WAAA,CAAY,MAAA,CAAS,CAAA,CAAG,CACnC,IAAMc,CAAAA,CAAa,IAAA,CAAK,YAAA,CAAaV,CAAAA,EAAc,CAAA,CAC7CW,EAAkBlM,CAAAA,CAAG,KAAA,CAAO,CAAE,KAAA,CAAO,yBAA0B,CAAC,CAAA,CACtEkM,CAAAA,CAAgB,WAAA,CAAYtM,CAAAA,CAAS4J,CAAY,CAAC,EAClD,IAAM2C,CAAAA,CAAenM,CAAAA,CAAG,MAAM,CAAA,CAC9BM,CAAAA,CAAQ6L,EAAc,IAAA,CAAK,CAAA,CAAE,mBAAmB,CAAC,CAAA,CACjDD,CAAAA,CAAgB,YAAYC,CAAY,CAAA,CACxCF,EAAW,WAAA,CAAYC,CAAe,EACtC,IAAA,CAAK,eAAA,CAAgBD,CAAAA,CAAYd,CAAQ,CAAA,CACzC,IAAA,CAAK,QAAQ,WAAA,CAAYc,CAAU,EACrC,CAKA,GAAItB,EAAAA,CAAeQ,EAAS,WAAW,CAAA,CAAG,CACxC,IAAMiB,CAAAA,CAAc,IAAA,CAAK,aAAab,CAAAA,EAAc,CAAA,CAC9Cc,EAAmBrM,CAAAA,CAAG,KAAA,CAAO,CAAE,KAAA,CAAO,yBAA0B,CAAC,CAAA,CACvEqM,CAAAA,CAAiB,WAAA,CAAYzM,EAASoK,EAAa,CAAC,CAAA,CACpD,IAAMsC,CAAAA,CAAgBtM,CAAAA,CAAG,MAAM,CAAA,CAC/BM,CAAAA,CAAQgM,CAAAA,CAAe,IAAA,CAAK,CAAA,CAAE,oBAAoB,CAAC,CAAA,CACnDD,CAAAA,CAAiB,YAAYC,CAAa,CAAA,CAC1CF,EAAY,WAAA,CAAYC,CAAgB,CAAA,CACxC,IAAA,CAAK,gBAAA,CAAiBD,CAAAA,CAAajB,CAAQ,CAAA,CAC3C,IAAA,CAAK,OAAA,CAAQ,WAAA,CAAYiB,CAAW,EACtC,CAGA,IAAA,CAAK,UAAA,CAAa,IAAA,CAClB,IAAA,CAAK,OAAA,CAAQ,YAAA,CAAa,cAAe,OAAO,CAAA,CAG3C,KAAK,OAAA,CAAQ,YAAA,CAClB,KAAK,OAAA,CAAQ,SAAA,CAAU,GAAA,CAAI,oBAAoB,CAAA,CAG/C,qBAAA,CAAsB,IAAM,CAC1BlB,CAAAA,CAAQ,KAAA,GACV,CAAC,EACH,CAGA,IAAA,EAAa,CACN,IAAA,CAAK,UAAA,GACV,IAAA,CAAK,UAAA,CAAa,MAClB,IAAA,CAAK,OAAA,CAAQ,SAAA,CAAU,MAAA,CAAO,oBAAoB,CAAA,CAClD,KAAK,OAAA,CAAQ,YAAA,CAAa,aAAA,CAAe,MAAM,CAAA,CAC/C,IAAA,CAAK,gBAAkB,IAAA,CACvB,IAAA,CAAK,UAAA,CAAa,IAAA,CAClB,IAAA,CAAK,SAAA,CAAY,MACnB,CAGA,IAAI,SAAA,EAAqB,CACvB,OAAO,IAAA,CAAK,UACd,CAGA,OAAA,EAAgB,CACd,IAAA,CAAK,IAAA,GACL,IAAA,CAAK,OAAA,CAAQ,MAAA,GACf,CAOQ,YAAA,CAAaqB,EAA4B,CAC/C,IAAMC,CAAAA,CAAUxM,CAAAA,CAAG,KAAA,CAAO,CAAE,MAAO,mBAAoB,CAAC,CAAA,CACxD,OAAAwM,CAAAA,CAAQ,KAAA,CAAM,eAAiB,CAAA,EAAGD,CAAAA,CAAQ,EAAE,CAAA,EAAA,CAAA,CACrCC,CACT,CAGQ,kBAAA,CAAmBjE,CAAAA,CAAwB4C,CAAAA,CAAkC,CACnF,IAAMsB,CAAAA,CAAatB,EAAS,MAAA,GAAW,UAAA,CAGjCuB,CAAAA,CAAe1M,CAAAA,CAAG,KAAA,CAAO,CAAE,MAAO,yBAA0B,CAAC,CAAA,CACnEM,CAAAA,CAAQoM,CAAAA,CAAc,IAAA,CAAK,EAAE,eAAe,CAAC,EAC7CnE,CAAAA,CAAU,WAAA,CAAYmE,CAAY,CAAA,CAGlC,IAAMC,CAAAA,CAAY3M,CAAAA,CAAG,KAAA,CAAO,CAAE,MAAO,kBAAmB,CAAC,CAAA,CACnD4M,CAAAA,CAAO5M,CAAAA,CAAG,MAAA,CAAQ,CACtB,KAAA,CAAO,CAAA,sBAAA,EAAyByM,CAAAA,CAAa,iCAAA,CAAoC,6BAA6B,CAAA,CAChH,CAAC,CAAA,CACKI,CAAAA,CAAM7M,EAAG,MAAA,CAAQ,CAAE,MAAO,sBAAuB,CAAC,CAAA,CACxD6M,CAAAA,CAAI,KAAA,CAAM,UAAA,CAAaJ,EAAa,SAAA,CAAY,SAAA,CAChDG,CAAAA,CAAK,WAAA,CAAYC,CAAG,CAAA,CACpB,IAAMC,CAAAA,CAAY9M,CAAAA,CAAG,MAAM,CAAA,CAC3BM,CAAAA,CAAQwM,CAAAA,CAAWL,EAAa,IAAA,CAAK,CAAA,CAAE,eAAe,CAAA,CAAI,IAAA,CAAK,EAAE,gBAAgB,CAAC,CAAA,CAElFnM,CAAAA,CAAQwM,CAAAA,CAAWL,CAAAA,CAAa,WAAa,MAAM,CAAA,CACnDG,CAAAA,CAAK,WAAA,CAAYE,CAAS,CAAA,CAC1BH,EAAU,WAAA,CAAYC,CAAI,CAAA,CAC1BrE,CAAAA,CAAU,WAAA,CAAYoE,CAAS,EAG/B,IAAM1E,CAAAA,CAAUjI,EAAG,KAAA,CAAO,CAAE,MAAO,mBAAoB,CAAC,CAAA,CAKxD,GAFA,IAAA,CAAK,UAAA,CAAa,SAAS,aAAA,CAAc,QAAQ,CAAA,CACjD,IAAA,CAAK,UAAA,CAAW,IAAA,CAAO,SACnByM,CAAAA,CAAY,CACd,IAAA,CAAK,UAAA,CAAW,SAAA,CAAY,sBAAA,CAC5B,KAAK,UAAA,CAAW,WAAA,CAAY7M,EAASuC,CAAS,CAAC,EAC/C,IAAM4K,CAAAA,CAAO,QAAA,CAAS,aAAA,CAAc,MAAM,CAAA,CAC1CzM,EAAQyM,CAAAA,CAAM,IAAA,CAAK,CAAA,CAAE,eAAe,CAAC,CAAA,CACrC,KAAK,UAAA,CAAW,WAAA,CAAYA,CAAI,EAClC,CAAA,KAAO,CACL,KAAK,UAAA,CAAW,SAAA,CAAY,wBAC5B,IAAA,CAAK,UAAA,CAAW,YAAYnN,CAAAA,CAAS+B,CAAU,CAAC,CAAA,CAChD,IAAMoL,CAAAA,CAAO,SAAS,aAAA,CAAc,MAAM,CAAA,CAC1CzM,CAAAA,CAAQyM,CAAAA,CAAM,IAAA,CAAK,EAAE,gBAAgB,CAAC,CAAA,CACtC,IAAA,CAAK,UAAA,CAAW,WAAA,CAAYA,CAAI,EAClC,CACA,KAAK,UAAA,CAAW,gBAAA,CAAiB,QAAS,IAAM,IAAA,CAAK,aAAA,EAAe,CAAA,CAGpE,IAAA,CAAK,UAAY,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA,CAChD,IAAA,CAAK,SAAA,CAAU,KAAO,QAAA,CACtB,IAAA,CAAK,SAAA,CAAU,SAAA,CAAY,sBAAA,CAC3B,IAAA,CAAK,UAAU,WAAA,CAAYnN,CAAAA,CAASwC,CAAU,CAAC,CAAA,CAC/C,IAAM4K,CAAAA,CAAa,QAAA,CAAS,aAAA,CAAc,MAAM,CAAA,CAChD1M,CAAAA,CAAQ0M,EAAY,IAAA,CAAK,CAAA,CAAE,eAAe,CAAC,CAAA,CAC3C,IAAA,CAAK,UAAU,WAAA,CAAYA,CAAU,CAAA,CACrC,IAAA,CAAK,SAAA,CAAU,gBAAA,CAAiB,QAAS,IAAM,IAAA,CAAK,cAAc,CAAA,CAElE/E,EAAQ,WAAA,CAAY,IAAA,CAAK,UAAU,CAAA,CACnCA,CAAAA,CAAQ,WAAA,CAAY,KAAK,SAAS,CAAA,CAClCM,CAAAA,CAAU,WAAA,CAAYN,CAAO,EAC/B,CAGQ,aAAA,CAAcM,CAAAA,CAAwB4C,CAAAA,CAAkC,CAC9E,IAAM8B,CAAAA,CAAOjN,EAAG,KAAA,CAAO,CAAE,MAAO,gBAAiB,CAAC,EA+ClD,GA5CA,IAAA,CAAK,UAAA,CAAWiN,CAAAA,CAAMxD,EAAAA,CAAW,IAAA,CAAK,EAAE,aAAa,CAAA,CAAG,IAAM,CAC5D,IAAMpJ,CAAAA,CAAQL,EAAG,KAAA,CAAO,CAAE,KAAA,CAAO,sBAAuB,CAAC,CAAA,CACnDkN,EAAW5C,EAAAA,CAAgBa,CAAAA,CAAS,GAAG,CAAA,CAC7C,OAAA7K,EAAQD,CAAAA,CAAOmK,CAAAA,CAAS0C,CAAAA,CAAU,EAAE,CAAC,CAAA,CACrC7M,EAAM,KAAA,CAAQ8K,CAAAA,CAAS,GAAA,CAChB9K,CACT,CAAC,CAAA,CAGD,KAAK,UAAA,CAAW4M,CAAAA,CAAMvD,EAAAA,CAAW,IAAA,CAAK,CAAA,CAAE,eAAe,EAAG,IAAM,CAC9D,IAAMrJ,CAAAA,CAAQL,CAAAA,CAAG,MAAO,CAAE,KAAA,CAAO,sBAAuB,CAAC,CAAA,CACnDmN,CAAAA,CAAOhC,EAAS,UAAA,EAAc,WAAA,CAC9BiC,CAAAA,CAAQjC,CAAAA,CAAS,WAAA,CACvB,OAAA7K,EAAQD,CAAAA,CAAO+M,CAAAA,CAAQ,CAAA,EAAGD,CAAI,CAAA,EAAA,EAAKC,CAAK,IAAMD,CAAI,CAAA,CAC3C9M,CACT,CAAC,CAAA,CAGD,KAAK,UAAA,CAAW4M,CAAAA,CAAMtD,EAAAA,CAAe,IAAA,CAAK,CAAA,CAAE,aAAa,EAAG,IAAM,CAChE,IAAMtJ,CAAAA,CAAQL,CAAAA,CAAG,KAAA,CAAO,CAAE,KAAA,CAAO,sBAAuB,CAAC,CAAA,CACzD,OAAAM,CAAAA,CAAQD,EAAOgK,CAAAA,CAAec,CAAAA,CAAS,SAAA,CAAW,IAAA,CAAK,MAAA,CAAO,UAAA,CAAW,IAAI,CAAA,CAAI,IAAA,CAAO,IAAI,CAAC,CAAA,CACtF9K,CACT,CAAC,CAAA,CAGD,IAAA,CAAK,UAAA,CAAW4M,CAAAA,CAAMrD,EAAAA,CAAc,IAAA,CAAK,EAAE,iBAAiB,CAAA,CAAG,IAAM,CACnE,IAAMvJ,CAAAA,CAAQL,EAAG,KAAA,CAAO,CAAE,MAAO,iDAAkD,CAAC,EACpF,OAAAM,CAAAA,CAAQD,CAAAA,CAAO8K,CAAAA,CAAS,QAAA,EAAY,SAAS,EACtC9K,CACT,CAAC,CAAA,CAGD,IAAA,CAAK,UAAA,CACH4M,CAAAA,CACA,mUACA,IAAA,CAAK,CAAA,CAAE,gBAAgB,CAAA,CACvB,IAAM,CACJ,IAAM5M,CAAAA,CAAQL,CAAAA,CAAG,MAAO,CAAE,KAAA,CAAO,sBAAuB,CAAC,CAAA,CACzD,OAAAM,CAAAA,CAAQD,CAAAA,CAAO6J,EAAAA,CAAaiB,EAAS,SAAS,CAAC,CAAA,CACxC9K,CACT,CACF,CAAA,CAGI8K,EAAS,UAAA,CAAY,CACvB,IAAMkC,CAAAA,CAAelC,CAAAA,CAAS,UAAA,CAC9B,KAAK,UAAA,CAAW8B,CAAAA,CAAMtL,EAAY,IAAA,CAAK,CAAA,CAAE,mBAAmB,CAAA,CAAG,IAAM,CACnE,IAAMtB,CAAAA,CAAQL,CAAAA,CAAG,MAAO,CAAE,KAAA,CAAO,sDAAuD,CAAC,CAAA,CACzF,OAAAM,EAAQD,CAAAA,CAAOgK,CAAAA,CAAegD,CAAAA,CAAc,IAAA,CAAK,MAAA,CAAO,UAAA,CAAW,IAAI,CAAA,CAAI,IAAA,CAAO,IAAI,CAAC,CAAA,CAChFhN,CACT,CAAC,EACH,CAEAkI,CAAAA,CAAU,WAAA,CAAY0E,CAAI,EAC5B,CAGQ,UAAA,CAAW1E,CAAAA,CAAwBrB,CAAAA,CAAiBJ,CAAAA,CAAewG,CAAAA,CAAqC,CAC9G,IAAMC,CAAAA,CAAMvN,CAAAA,CAAG,KAAA,CAAO,CAAE,KAAA,CAAO,oBAAqB,CAAC,CAAA,CACrDuN,EAAI,WAAA,CAAY3N,CAAAA,CAASsH,CAAO,CAAC,CAAA,CAEjC,IAAMb,CAAAA,CAAUrG,CAAAA,CAAG,KAAA,CAAO,CAAE,KAAA,CAAO,wBAAyB,CAAC,CAAA,CACvDuH,CAAAA,CAAUvH,CAAAA,CAAG,MAAO,CAAE,KAAA,CAAO,sBAAuB,CAAC,CAAA,CAC3DM,CAAAA,CAAQiH,EAAST,CAAK,CAAA,CACtBT,EAAQ,WAAA,CAAYkB,CAAO,EAC3BlB,CAAAA,CAAQ,WAAA,CAAYiH,CAAAA,EAAY,CAAA,CAEhCC,CAAAA,CAAI,YAAYlH,CAAO,CAAA,CACvBkC,CAAAA,CAAU,WAAA,CAAYgF,CAAG,EAC3B,CAGQ,eAAA,CAAgBhF,CAAAA,CAAwB4C,CAAAA,CAAkC,CAChF,IAAM3G,CAAAA,CAAM2G,EAAS,WAAA,CAAY,CAAC,EAClC,GAAI,CAAC3G,EAAK,OAEV,IAAM4D,CAAAA,CAAUpI,CAAAA,CAAG,KAAA,CAAO,CAAE,MAAO,sBAAuB,CAAC,CAAA,CAGrDwN,CAAAA,CAAOxN,CAAAA,CAAG,KAAA,CAAO,CAAE,KAAA,CAAO,2BAA4B,CAAC,CAAA,CAG7D,IAAA,CAAK,gBAAA,CAAiBwN,EAAM3D,EAAAA,CAAW,IAAA,CAAK,EAAE,gBAAgB,CAAA,CAAG,IAAM,CACrE,IAAMxJ,CAAAA,CAAQL,CAAAA,CAAG,MAAA,CAAQ,CAAE,MAAO,6DAA8D,CAAC,CAAA,CAC3FyN,CAAAA,CAAajJ,CAAAA,CAAI,SAAA,CAAY,IAAIA,CAAAA,CAAI,UAAU,CAAA,CAAA,EAAIA,CAAAA,CAAI,SAAS,CAAA,CAAA,CAAA,CAAM,IAAIA,CAAAA,CAAI,UAAU,IAC9F,OAAAlE,CAAAA,CAAQD,EAAOoN,CAAU,CAAA,CAClBpN,CACT,CAAC,CAAA,CAGD,IAAA,CAAK,iBAAiBmN,CAAAA,CAAM1D,EAAAA,CAAgB,IAAA,CAAK,CAAA,CAAE,iBAAiB,CAAA,CAAG,IAAM,CAC3E,IAAMzJ,CAAAA,CAAQL,CAAAA,CAAG,MAAA,CAAQ,CAAE,MAAO,6DAA8D,CAAC,EACjG,OAAAM,CAAAA,CAAQD,EAAOmK,CAAAA,CAAShG,CAAAA,CAAI,WAAA,CAAa,EAAE,CAAC,CAAA,CAC5CnE,EAAM,KAAA,CAAQmE,CAAAA,CAAI,WAAA,CACXnE,CACT,CAAC,CAAA,CAGD,KAAK,gBAAA,CAAiBmN,CAAAA,CAAMhE,CAAAA,CAAc,IAAA,CAAK,CAAA,CAAE,iBAAiB,EAAG,IAAM,CACzE,IAAMnJ,CAAAA,CAAQL,CAAAA,CAAG,OAAQ,CAAE,KAAA,CAAO,4BAA6B,CAAC,CAAA,CAChE,OAAAM,EACED,CAAAA,CACA,CAAA,EAAGmE,CAAAA,CAAI,IAAA,CAAK,OAAA,CAAQ,CAAC,CAAC,CAAA,GAAA,EAAMA,CAAAA,CAAI,IAAA,CAAK,OAAA,CAAQ,CAAC,CAAC,KAC5CA,CAAAA,CAAI,IAAA,CAAO,GAAKA,CAAAA,CAAI,IAAA,CAAO,EAAI,CAAA,EAAA,EAAKA,CAAAA,CAAI,IAAA,CAAK,OAAA,CAAQ,CAAC,CAAC,UAAYA,CAAAA,CAAI,IAAA,CAAK,OAAA,CAAQ,CAAC,CAAC,CAAA,EAAA,CAAA,CAAO,GAClG,CAAA,CACOnE,CACT,CAAC,CAAA,CAED+H,CAAAA,CAAQ,WAAA,CAAYoF,CAAI,CAAA,CAGxB,IAAME,EAAU,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA,CAC/CA,CAAAA,CAAQ,IAAA,CAAO,QAAA,CACfA,CAAAA,CAAQ,SAAA,CAAY,qBACpBA,CAAAA,CAAQ,WAAA,CAAY9N,CAAAA,CAAS4J,CAAY,CAAC,CAAA,CAC1C,IAAMmE,CAAAA,CAAY,QAAA,CAAS,aAAA,CAAc,MAAM,CAAA,CAC/CrN,CAAAA,CAAQqN,EAAW,IAAA,CAAK,CAAA,CAAE,uBAAuB,CAAC,CAAA,CAClDD,EAAQ,WAAA,CAAYC,CAAS,CAAA,CAC7BD,CAAAA,CAAQ,gBAAA,CAAiB,OAAA,CAAS,IAAM,CAClC,IAAA,CAAK,eAAA,EACP,IAAA,CAAK,SAAA,CAAU,gBAAA,CAAiB,KAAK,eAAe,EAExD,CAAC,CAAA,CAEDtF,CAAAA,CAAQ,WAAA,CAAYsF,CAAO,CAAA,CAC3BnF,CAAAA,CAAU,YAAYH,CAAO,EAC/B,CASQ,gBAAA,CAAiBG,CAAAA,CAAwB4C,CAAAA,CAAkC,CACjF,IAAMyC,CAAAA,CAAOzC,EAAS,WAAA,CACtB,GAAI,CAACyC,CAAAA,CAAM,OAEX,IAAMC,EAAiB,KAAA,CAAM,OAAA,CAAQD,CAAAA,CAAK,OAAO,CAAA,CAAIA,CAAAA,CAAK,QAAU,EAAC,CAC/DE,CAAAA,CAAiB,KAAA,CAAM,OAAA,CAAQF,CAAAA,CAAK,OAAO,CAAA,CAAIA,CAAAA,CAAK,OAAA,CAAU,EAAC,CAC/DG,CAAAA,CAAaF,EAAe,MAAA,CAAQ9G,CAAAA,EAAMA,CAAAA,CAAE,KAAA,GAAU,OAAO,CAAA,CAAE,OAE/DqB,CAAAA,CAAUpI,CAAAA,CAAG,KAAA,CAAO,CAAE,KAAA,CAAO,gBAAiB,CAAC,CAAA,CAE/CgO,CAAAA,CAAS,SAAS,aAAA,CAAc,QAAQ,EAC9CA,CAAAA,CAAO,IAAA,CAAO,QAAA,CACdA,CAAAA,CAAO,SAAA,CAAY,uBAAA,CACnBA,EAAO,YAAA,CAAa,eAAA,CAAiB,OAAO,CAAA,CAC5CA,CAAAA,CAAO,YAAA,CAAa,aAAc,IAAA,CAAK,CAAA,CAAE,2BAA2B,CAAC,CAAA,CACrE,IAAMC,EAAc,QAAA,CAAS,aAAA,CAAc,MAAM,CAAA,CAC3CC,CAAAA,CAAU,SAAS,aAAA,CAAc,MAAM,CAAA,CAC7CA,CAAAA,CAAQ,KAAA,CAAM,OAAA,CAAU,cACxBA,CAAAA,CAAQ,KAAA,CAAM,UAAA,CAAa,QAAA,CAC3BA,CAAAA,CAAQ,KAAA,CAAM,IAAM,KAAA,CACpBA,CAAAA,CAAQ,WAAA,CAAYtO,CAAAA,CAASmK,EAAY,CAAC,EAC1CzJ,CAAAA,CAAQ2N,CAAAA,CAAa,KAAK,CAAA,CAAE,oBAAoB,CAAC,CAAA,CACjDC,CAAAA,CAAQ,WAAA,CAAYD,CAAW,CAAA,CAC/BD,CAAAA,CAAO,YAAYE,CAAO,CAAA,CAE1B,IAAMC,CAAAA,CAASnO,CAAAA,CAAG,MAAA,CAAQ,CAAE,KAAA,CAAO,uBAAwB,CAAC,CAAA,CACtDoO,CAAAA,CAAepO,CAAAA,CAAG,OAAQ,CAC9B,KAAA,CAAO,uBAAuB+N,CAAAA,CAAa,CAAA,CAAI,gCAAkC,EAAE,CAAA,CACrF,CAAC,CAAA,CACDzN,CAAAA,CAAQ8N,CAAAA,CAAc,GAAGP,CAAAA,CAAe,MAAM,CAAA,QAAA,CAAU,CAAA,CACxD,IAAMQ,CAAAA,CAAerO,EAAG,MAAA,CAAQ,CAC9B,KAAA,CAAO,CAAA,oBAAA,EAAuB8N,CAAAA,CAAe,MAAA,CAAS,EAAI,+BAAA,CAAkC,EAAE,EAChG,CAAC,CAAA,CACDxN,EAAQ+N,CAAAA,CAAc,CAAA,EAAGP,CAAAA,CAAe,MAAM,CAAA,IAAA,CAAM,CAAA,CACpDK,EAAO,WAAA,CAAYC,CAAY,CAAA,CAC/BD,CAAAA,CAAO,WAAA,CAAYE,CAAY,EAC/BL,CAAAA,CAAO,WAAA,CAAYG,CAAM,CAAA,CAEzB,IAAMG,CAAAA,CAAOtO,EAAG,KAAA,CAAO,CAAE,MAAO,qBAAsB,CAAC,EAGvD,GAAI6N,CAAAA,CAAe,MAAA,CAAS,CAAA,CAAG,CAC7B,IAAMU,EAAQ,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA,CACpClD,CAAAA,CAAQrL,CAAAA,CAAG,MAAO,CAAE,KAAA,CAAO,4BAA6B,CAAC,CAAA,CAC/DM,CAAAA,CAAQ+K,EAAO,IAAA,CAAK,CAAA,CAAE,4BAA4B,CAAC,CAAA,CACnDkD,EAAM,WAAA,CAAYlD,CAAK,CAAA,CAEvB,IAAMmD,CAAAA,CAAO,QAAA,CAAS,cAAc,IAAI,CAAA,CACxCA,CAAAA,CAAK,SAAA,CAAY,qBAAA,CACjB,IAAA,IAAWC,KAASZ,CAAAA,CAAgB,CAClC,IAAMa,CAAAA,CAAO,QAAA,CAAS,aAAA,CAAc,IAAI,CAAA,CAClCC,CAAAA,CAAQ3O,EAAG,MAAA,CAAQ,CACvB,MAAO,CAAA,2CAAA,EAA8CyO,CAAAA,CAAM,KAAK,CAAA,CAClE,CAAC,CAAA,CACDnO,EAAQqO,CAAAA,CAAOF,CAAAA,CAAM,KAAK,CAAA,CAC1B,IAAMG,CAAAA,CAAM5O,EAAG,MAAA,CAAQ,CAAE,KAAA,CAAO,wBAAyB,CAAC,CAAA,CAE1DM,EAAQsO,CAAAA,CAAKpE,CAAAA,CAASiE,EAAM,OAAA,CAAS,GAAG,CAAC,CAAA,CACzCG,CAAAA,CAAI,KAAA,CAAQH,CAAAA,CAAM,OAAA,CAClBC,CAAAA,CAAK,YAAYC,CAAK,CAAA,CACtBD,CAAAA,CAAK,WAAA,CAAYE,CAAG,CAAA,CACpBJ,EAAK,WAAA,CAAYE,CAAI,EACvB,CACAH,CAAAA,CAAM,WAAA,CAAYC,CAAI,CAAA,CACtBF,CAAAA,CAAK,YAAYC,CAAK,EACxB,CAGA,GAAIT,CAAAA,CAAe,MAAA,CAAS,CAAA,CAAG,CAC7B,IAAMS,EAAQ,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA,CACpClD,CAAAA,CAAQrL,CAAAA,CAAG,MAAO,CAAE,KAAA,CAAO,4BAA6B,CAAC,CAAA,CAC/DM,CAAAA,CAAQ+K,EAAO,IAAA,CAAK,CAAA,CAAE,4BAA4B,CAAC,CAAA,CACnDkD,EAAM,WAAA,CAAYlD,CAAK,CAAA,CAEvB,IAAMmD,CAAAA,CAAO,QAAA,CAAS,cAAc,IAAI,CAAA,CACxCA,CAAAA,CAAK,SAAA,CAAY,qBAAA,CACjB,IAAA,IAAWC,KAASX,CAAAA,CAAgB,CAClC,IAAMY,CAAAA,CAAO,QAAA,CAAS,aAAA,CAAc,IAAI,CAAA,CACxCA,CAAAA,CAAK,UAAU,GAAA,CAAI,oBAAoB,EACvC,IAAMG,CAAAA,CAAS7O,CAAAA,CAAG,MAAA,CAAQ,CAAE,KAAA,CAAO,2BAA4B,CAAC,CAAA,CAChEM,CAAAA,CAAQuO,CAAAA,CAAQJ,CAAAA,CAAM,MAAA,GAAW,EAAI,KAAA,CAAQ,MAAA,CAAOA,CAAAA,CAAM,MAAM,CAAC,CAAA,CACjE,IAAMK,CAAAA,CAAS9O,CAAAA,CAAG,OAAQ,CAAE,KAAA,CAAO,2BAA4B,CAAC,CAAA,CAChEM,CAAAA,CAAQwO,CAAAA,CAAQL,CAAAA,CAAM,MAAM,EAC5B,IAAMhI,CAAAA,CAAMzG,CAAAA,CAAG,MAAA,CAAQ,CAAE,KAAA,CAAO,wBAAyB,CAAC,CAAA,CAC1DM,CAAAA,CAAQmG,CAAAA,CAAK+D,CAAAA,CAASiE,CAAAA,CAAM,IAAK,GAAG,CAAC,EACrChI,CAAAA,CAAI,KAAA,CAAQ,GAAGgI,CAAAA,CAAM,GAAG,CAAA,QAAA,EAAM1D,EAAAA,CAAe0D,CAAAA,CAAM,UAAU,CAAC,CAAA,CAAA,CAC9DC,CAAAA,CAAK,WAAA,CAAYG,CAAM,CAAA,CACvBH,CAAAA,CAAK,YAAYI,CAAM,CAAA,CACvBJ,CAAAA,CAAK,WAAA,CAAYjI,CAAG,CAAA,CACpB+H,EAAK,WAAA,CAAYE,CAAI,EACvB,CACAH,CAAAA,CAAM,YAAYC,CAAI,CAAA,CACtBF,CAAAA,CAAK,WAAA,CAAYC,CAAK,EACxB,CAEAP,CAAAA,CAAO,gBAAA,CAAiB,OAAA,CAAS,IAAM,CAErC,IAAMe,EAAO,EADIf,CAAAA,CAAO,YAAA,CAAa,eAAe,CAAA,GAAM,MAAA,CAAA,CAE1DA,EAAO,YAAA,CAAa,eAAA,CAAiB,MAAA,CAAOe,CAAI,CAAC,CAAA,CACjDf,EAAO,YAAA,CACL,YAAA,CACAe,CAAAA,CAAO,IAAA,CAAK,CAAA,CAAE,6BAA6B,EAAI,IAAA,CAAK,CAAA,CAAE,2BAA2B,CACnF,CAAA,CACAT,CAAAA,CAAK,UAAU,MAAA,CAAO,2BAAA,CAA6BS,CAAI,EACzD,CAAC,CAAA,CAED3G,EAAQ,WAAA,CAAY4F,CAAM,EAC1B5F,CAAAA,CAAQ,WAAA,CAAYkG,CAAI,CAAA,CACxB/F,CAAAA,CAAU,WAAA,CAAYH,CAAO,EAC/B,CAGQ,iBACNG,CAAAA,CACArB,CAAAA,CACAJ,CAAAA,CACAwG,CAAAA,CACM,CACN,IAAMC,EAAMvN,CAAAA,CAAG,KAAA,CAAO,CAAE,KAAA,CAAO,0BAA2B,CAAC,EAC3DuN,CAAAA,CAAI,WAAA,CAAY3N,EAASsH,CAAO,CAAC,EAEjC,IAAMb,CAAAA,CAAUrG,CAAAA,CAAG,KAAA,CAAO,CAAE,KAAA,CAAO,wBAAyB,CAAC,CAAA,CACvDuH,CAAAA,CAAUvH,CAAAA,CAAG,KAAA,CAAO,CAAE,MAAO,4BAA6B,CAAC,CAAA,CACjEM,CAAAA,CAAQiH,CAAAA,CAAST,CAAK,EACtBT,CAAAA,CAAQ,WAAA,CAAYkB,CAAO,CAAA,CAC3BlB,CAAAA,CAAQ,YAAYiH,CAAAA,EAAY,CAAA,CAEhCC,CAAAA,CAAI,WAAA,CAAYlH,CAAO,EACvBkC,CAAAA,CAAU,WAAA,CAAYgF,CAAG,EAC3B,CAMA,MAAc,eAA+B,CAC3C,GAAI,EAAA,IAAA,CAAK,YAAA,EAAgB,CAAC,IAAA,CAAK,iBAC/B,CAAA,IAAA,CAAK,YAAA,CAAe,KAEhB,IAAA,CAAK,UAAA,EAAY,KAAK,gBAAA,CAAiB,IAAA,CAAK,UAAU,CAAA,CACtD,IAAA,CAAK,SAAA,GAAW,KAAK,SAAA,CAAU,QAAA,CAAW,IAAA,CAAA,CAE9C,GAAI,CACF,MAAM,KAAK,SAAA,CAAU,SAAA,CAAU,IAAA,CAAK,eAAe,EAErD,CAAA,KAAQ,CAEN,IAAA,CAAK,YAAA,CAAe,MAChB,IAAA,CAAK,UAAA,EAAY,KAAK,iBAAA,CAAkB,IAAA,CAAK,eAAe,CAAA,CAC5D,IAAA,CAAK,SAAA,GAAW,KAAK,SAAA,CAAU,QAAA,CAAW,KAAA,EAChD,CAAA,CACF,CAEA,MAAc,cAA8B,CAC1C,GAAI,EAAA,IAAA,CAAK,YAAA,EAAgB,CAAC,IAAA,CAAK,iBAC/B,CAAA,IAAA,CAAK,YAAA,CAAe,KAEhB,IAAA,CAAK,SAAA,EAAW,KAAK,gBAAA,CAAiB,IAAA,CAAK,SAAS,CAAA,CACpD,IAAA,CAAK,UAAA,GAAY,KAAK,UAAA,CAAW,QAAA,CAAW,IAAA,CAAA,CAEhD,GAAI,CACF,MAAM,KAAK,SAAA,CAAU,QAAA,CAAS,IAAA,CAAK,eAAe,EAEpD,CAAA,KAAQ,CACN,IAAA,CAAK,YAAA,CAAe,MAChB,IAAA,CAAK,SAAA,EAAW,KAAK,gBAAA,EAAiB,CACtC,IAAA,CAAK,UAAA,GAAY,IAAA,CAAK,UAAA,CAAW,SAAW,KAAA,EAClD,CAAA,CACF,CAEQ,gBAAA,CAAiB9M,CAAAA,CAA8B,CACrDA,EAAI,QAAA,CAAW,IAAA,CACfA,CAAAA,CAAI,eAAA,CAAgBT,CAAAA,CAAG,KAAA,CAAO,CAAE,KAAA,CAAO,2BAA4B,CAAC,CAAC,EACvE,CAEQ,iBAAA,CAAkBmL,CAAAA,CAAkC,CAC1D,GAAI,CAAC,IAAA,CAAK,WAAY,OACtB,IAAA,CAAK,UAAA,CAAW,QAAA,CAAW,KAAA,CAC3B,IAAA,CAAK,WAAW,eAAA,EAAgB,CAEhC,IAAMsB,CAAAA,CAAatB,CAAAA,CAAS,MAAA,GAAW,WACvC,IAAA,CAAK,UAAA,CAAW,YAAYvL,CAAAA,CAAS6M,CAAAA,CAAatK,EAAYR,CAAU,CAAC,CAAA,CACzE,IAAMoL,CAAAA,CAAO,QAAA,CAAS,cAAc,MAAM,CAAA,CAC1CzM,CAAAA,CAAQyM,CAAAA,CAAMN,CAAAA,CAAa,IAAA,CAAK,EAAE,eAAe,CAAA,CAAI,IAAA,CAAK,CAAA,CAAE,gBAAgB,CAAC,EAC7E,IAAA,CAAK,UAAA,CAAW,YAAYM,CAAI,EAClC,CAEQ,gBAAA,EAAyB,CAC/B,GAAI,CAAC,IAAA,CAAK,SAAA,CAAW,OACrB,IAAA,CAAK,SAAA,CAAU,QAAA,CAAW,KAAA,CAC1B,IAAA,CAAK,SAAA,CAAU,iBAAgB,CAC/B,IAAA,CAAK,SAAA,CAAU,WAAA,CAAYnN,CAAAA,CAASwC,CAAU,CAAC,CAAA,CAC/C,IAAM2K,EAAO,QAAA,CAAS,aAAA,CAAc,MAAM,CAAA,CAC1CzM,CAAAA,CAAQyM,CAAAA,CAAM,IAAA,CAAK,CAAA,CAAE,eAAe,CAAC,CAAA,CACrC,IAAA,CAAK,SAAA,CAAU,WAAA,CAAYA,CAAI,EACjC,CACF,ECt6CO,IAAMiC,EAAAA,CAAY,6PAAA,CAEZC,EAAAA,CAAY,4WAAA,CAEZlF,GAAe,yLAAA,CAOtBmF,EAAAA,CAA2C,CAC/C,QAAA,CAAU,CAAA,CACV,OAAQ,CAAA,CACR,GAAA,CAAK,CAAA,CACL,KAAA,CAAO,CACT,CAAA,CAGO,SAASC,EAAAA,CAAcrJ,CAAAA,CAA+BsJ,CAAAA,CAAoC,CAC/F,IAAMC,CAAAA,CAAS,CAAC,GAAGvJ,CAAS,CAAA,CAE5B,OAAQsJ,CAAAA,EACN,KAAK,QAAA,CACHC,CAAAA,CAAO,KAAK,CAACC,CAAAA,CAAGrM,IAAM,IAAI,IAAA,CAAKA,CAAAA,CAAE,SAAS,CAAA,CAAE,OAAA,GAAY,IAAI,IAAA,CAAKqM,CAAAA,CAAE,SAAS,CAAA,CAAE,OAAA,EAAS,CAAA,CACvF,MAEF,KAAK,QAAA,CACHD,CAAAA,CAAO,IAAA,CAAK,CAACC,CAAAA,CAAGrM,CAAAA,GAAM,IAAI,IAAA,CAAKqM,CAAAA,CAAE,SAAS,CAAA,CAAE,OAAA,EAAQ,CAAI,IAAI,IAAA,CAAKrM,CAAAA,CAAE,SAAS,CAAA,CAAE,OAAA,EAAS,CAAA,CACvF,MAEF,KAAK,UACHoM,CAAAA,CAAO,IAAA,CAAK,CAACC,CAAAA,CAAGrM,CAAAA,GAAM,CACpB,IAAMsM,CAAAA,CAAQL,EAAAA,CAAWI,EAAE,IAAI,CAAA,EAAK,GAC9BE,CAAAA,CAAQN,EAAAA,CAAWjM,CAAAA,CAAE,IAAI,CAAA,EAAK,EAAA,CACpC,OAAIsM,CAAAA,GAAUC,CAAAA,CAAcD,CAAAA,CAAQC,CAAAA,CAE7B,IAAI,IAAA,CAAKvM,EAAE,SAAS,CAAA,CAAE,OAAA,EAAQ,CAAI,IAAI,IAAA,CAAKqM,EAAE,SAAS,CAAA,CAAE,OAAA,EACjE,CAAC,CAAA,CACD,MAEF,KAAK,YAAA,CACHD,CAAAA,CAAO,IAAA,CAAK,CAACC,CAAAA,CAAGrM,IAAM,CAEpB,IAAMwM,CAAAA,CAAUH,CAAAA,CAAE,MAAA,GAAW,MAAA,CAAS,EAAI,CAAA,CACpCI,CAAAA,CAAUzM,CAAAA,CAAE,MAAA,GAAW,MAAA,CAAS,CAAA,CAAI,EAC1C,OAAIwM,CAAAA,GAAYC,EAAgBD,CAAAA,CAAUC,CAAAA,CAEnC,IAAI,IAAA,CAAKzM,CAAAA,CAAE,SAAS,CAAA,CAAE,OAAA,EAAQ,CAAI,IAAI,IAAA,CAAKqM,CAAAA,CAAE,SAAS,CAAA,CAAE,OAAA,EACjE,CAAC,CAAA,CACD,KACJ,CAEA,OAAOD,CACT,CAUA,SAAS/E,EAAAA,CAAgB7D,CAAAA,CAAqB,CAC5C,GAAI,CACF,OAAO,IAAI,GAAA,CAAIA,CAAG,CAAA,CAAE,QACtB,CAAA,KAAQ,CACN,OAAOA,CACT,CACF,CAGA,SAASkJ,EAAAA,CAAaC,EAAcC,CAAAA,CAA2B,CAC7D,GAAID,CAAAA,CAAK,MAAA,EAAUC,CAAAA,CAAW,OAAOD,CAAAA,CACrC,IAAME,EAAW,QAAA,CACXC,CAAAA,CAAO,KAAK,KAAA,CAAA,CAAOF,CAAAA,CAAY,CAAA,EAAK,CAAC,CAAA,CAC3C,OAAOD,EAAK,KAAA,CAAM,CAAA,CAAGG,CAAI,CAAA,CAAID,CAAAA,CAAWF,CAAAA,CAAK,MAAM,CAACG,CAAI,CAC1D,CAOO,SAASC,EAAAA,CAAqBlK,EAAgE,CACnG,IAAMmK,EAAS,IAAI,GAAA,CAEnB,QAAWhK,CAAAA,IAAMH,CAAAA,CAAW,CAC1B,IAAM8J,CAAAA,CAAOtF,EAAAA,CAAgBrE,EAAG,GAAG,CAAA,CAC7BiK,CAAAA,CAAWD,CAAAA,CAAO,GAAA,CAAIL,CAAI,EAC5BM,CAAAA,CACFA,CAAAA,CAAS,IAAA,CAAKjK,CAAE,CAAA,CAEhBgK,CAAAA,CAAO,IAAIL,CAAAA,CAAM,CAAC3J,CAAE,CAAC,EAEzB,CAKA,OAFe,IAAI,GAAA,CAAI,CAAC,GAAGgK,CAAAA,CAAO,SAAS,CAAA,CAAE,IAAA,CAAK,CAACX,CAAAA,CAAGrM,CAAAA,GAAMA,EAAE,CAAC,CAAA,CAAE,MAAA,CAASqM,CAAAA,CAAE,CAAC,CAAA,CAAE,MAAM,CAAC,CAGxF,CAUO,SAASa,EAAAA,CAAsBC,EAAkB1H,CAAAA,CAAehF,CAAAA,CAAkC,CACvG,IAAMqC,CAAAA,CAAS/F,CAAAA,CAAG,MAAO,CAAE,KAAA,CAAO,iBAAkB,CAAC,CAAA,CACrD+F,CAAAA,CAAO,aAAa,MAAA,CAAQ,QAAQ,CAAA,CACpCA,CAAAA,CAAO,YAAA,CAAa,UAAA,CAAY,GAAG,CAAA,CACnCA,CAAAA,CAAO,aAAa,eAAA,CAAiB,MAAM,EAC3CA,CAAAA,CAAO,KAAA,CAAM,iBAAA,CAAoBrC,CAAAA,CAAO,MAAA,CAGxC,IAAM2M,EAAcrQ,CAAAA,CAAG,MAAA,CAAQ,CAAE,KAAA,CAAO,yBAA0B,CAAC,EACnEqQ,CAAAA,CAAY,WAAA,CAAYzQ,CAAAA,CAASmK,EAAY,CAAC,CAAA,CAC9ChE,EAAO,WAAA,CAAYsK,CAAW,EAG9B,IAAMC,CAAAA,CAAWtQ,EAAG,MAAA,CAAQ,CAAE,KAAA,CAAO,sBAAuB,CAAC,CAAA,CAC7DsQ,EAAS,WAAA,CAAY1Q,CAAAA,CAASqP,EAAS,CAAC,CAAA,CACxClJ,CAAAA,CAAO,YAAYuK,CAAQ,CAAA,CAG3B,IAAMC,CAAAA,CAASvQ,CAAAA,CAAG,MAAA,CAAQ,CAAE,KAAA,CAAO,sBAAuB,CAAC,CAAA,CACrDwQ,CAAAA,CAAcb,GAAaS,CAAAA,CAAU,EAAE,CAAA,CAC7C9P,CAAAA,CAAQiQ,CAAAA,CAAQC,CAAW,EACvBJ,CAAAA,CAAS,MAAA,CAAS,EAAA,GACpBG,CAAAA,CAAO,KAAA,CAAQH,CAAAA,CAAAA,CAEjBrK,EAAO,WAAA,CAAYwK,CAAM,CAAA,CAGzB,IAAME,CAAAA,CAAUzQ,CAAAA,CAAG,OAAQ,CAAE,KAAA,CAAO,uBAAwB,CAAC,CAAA,CAC7DyQ,EAAQ,KAAA,CAAM,UAAA,CAAa/M,CAAAA,CAAO,WAAA,CAClC+M,CAAAA,CAAQ,KAAA,CAAM,MAAQ/M,CAAAA,CAAO,MAAA,CAC7BpD,CAAAA,CAAQmQ,CAAAA,CAAS,MAAA,CAAO/H,CAAK,CAAC,CAAA,CAC9B3C,CAAAA,CAAO,WAAA,CAAY0K,CAAO,CAAA,CAG1B,IAAMzC,EAAS,IAAM,CACnB,IAAM0C,CAAAA,CAAa3K,CAAAA,CAAO,aAAa,eAAe,CAAA,GAAM,MAAA,CAC5DA,CAAAA,CAAO,YAAA,CAAa,eAAA,CAAiB,OAAO,CAAC2K,CAAU,CAAC,CAAA,CACxD3K,CAAAA,CAAO,SAAA,CAAU,OAAO,4BAAA,CAA8B2K,CAAU,CAAA,CAEhE,IAAMrK,CAAAA,CAAUN,CAAAA,CAAO,mBACnBM,CAAAA,EAAS,SAAA,CAAU,SAAS,kBAAkB,CAAA,EAChDA,EAAQ,SAAA,CAAU,MAAA,CAAO,6BAAA,CAA+BqK,CAAU,EAEtE,CAAA,CAEA,OAAA3K,CAAAA,CAAO,gBAAA,CAAiB,OAAA,CAASiI,CAAM,CAAA,CACvCjI,CAAAA,CAAO,iBAAiB,SAAA,CAAYgB,CAAAA,EAAM,CAAA,CACpCA,CAAAA,CAAE,GAAA,GAAQ,OAAA,EAAWA,EAAE,GAAA,GAAQ,GAAA,IACjCA,EAAE,cAAA,EAAe,CACjBiH,GAAO,EAEX,CAAC,CAAA,CAEMjI,CACT,CAMO,IAAM4K,GAAN,KAAwB,CACpB,OAAA,CAED,SAAA,CAAsB,QAAA,CACtB,YAAA,CAAe,MACf,MAAA,CAA6B,IAAA,CAC7B,OAAA,CACA,WAAA,CACS,CAAA,CACA,MAAA,CACA,SACT,mBAAA,CAAwD,IAAA,CAEhE,YAAYjN,CAAAA,CAAqBkN,CAAAA,CAAsBtL,EAAc,CACnE,IAAA,CAAK,MAAA,CAAS5B,CAAAA,CACd,IAAA,CAAK,QAAA,CAAWkN,EAChB,IAAA,CAAK,CAAA,CAAItL,CAAAA,CAET,IAAA,CAAK,OAAA,CAAUtF,CAAAA,CAAG,MAAO,CAAE,KAAA,CAAO,kBAAmB,CAAC,CAAA,CAGtD,IAAA,CAAK,QAAU,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA,CAC9C,IAAA,CAAK,QAAQ,SAAA,CAAY,aAAA,CACzB,IAAA,CAAK,OAAA,CAAQ,YAAA,CAAa,eAAA,CAAiB,SAAS,CAAA,CACpD,IAAA,CAAK,OAAA,CAAQ,YAAA,CAAa,eAAA,CAAiB,OAAO,EAClD,IAAA,CAAK,OAAA,CAAQ,YAAA,CAAa,YAAA,CAAc,IAAA,CAAK,CAAA,CAAE,YAAY,CAAC,CAAA,CAE5D,IAAM6Q,CAAAA,CAAWjR,CAAAA,CAASoP,EAAS,EACnC,IAAA,CAAK,OAAA,CAAQ,WAAA,CAAY6B,CAAQ,CAAA,CAEjC,IAAMC,EAAY9Q,CAAAA,CAAG,MAAA,CAAQ,CAAE,KAAA,CAAO,mBAAoB,CAAC,EAC3DM,CAAAA,CAAQwQ,CAAAA,CAAW,IAAA,CAAK,CAAA,CAAE,aAAa,CAAC,EACxC,IAAA,CAAK,OAAA,CAAQ,YAAYA,CAAS,CAAA,CAElC,KAAK,OAAA,CAAQ,gBAAA,CAAiB,OAAA,CAAU/J,CAAAA,EAAM,CAC5CA,CAAAA,CAAE,iBAAgB,CAClB,IAAA,CAAK,UAAA,GACP,CAAC,CAAA,CAGD,KAAK,WAAA,CAAc,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA,CAClD,IAAA,CAAK,YAAY,SAAA,CAAY,iBAAA,CAC7B,KAAK,WAAA,CAAY,YAAA,CAAa,eAAgB,OAAO,CAAA,CAErD,IAAMgK,CAAAA,CAAYnR,CAAAA,CAASqP,EAAS,EACpC,IAAA,CAAK,WAAA,CAAY,WAAA,CAAY8B,CAAS,CAAA,CAEtC,IAAMC,EAAahR,CAAAA,CAAG,MAAA,CAAQ,CAAE,KAAA,CAAO,uBAAwB,CAAC,EAChEM,CAAAA,CAAQ0Q,CAAAA,CAAY,KAAK,CAAA,CAAE,cAAc,CAAC,CAAA,CAC1C,IAAA,CAAK,WAAA,CAAY,WAAA,CAAYA,CAAU,CAAA,CAEvC,KAAK,WAAA,CAAY,gBAAA,CAAiB,OAAA,CAAS,IAAM,CAC/C,IAAA,CAAK,aAAe,CAAC,IAAA,CAAK,YAAA,CAC1B,IAAA,CAAK,WAAA,CAAY,SAAA,CAAU,OAAO,yBAAA,CAA2B,IAAA,CAAK,YAAY,CAAA,CAC9E,IAAA,CAAK,YAAY,YAAA,CAAa,cAAA,CAAgB,MAAA,CAAO,IAAA,CAAK,YAAY,CAAC,EACvE,IAAA,CAAK,QAAA,GACP,CAAC,CAAA,CAED,IAAA,CAAK,QAAQ,WAAA,CAAY,IAAA,CAAK,OAAO,CAAA,CACrC,IAAA,CAAK,OAAA,CAAQ,YAAY,IAAA,CAAK,WAAW,EAC3C,CAEA,IAAI,UAAqB,CACvB,OAAO,IAAA,CAAK,SACd,CAEA,IAAI,aAAuB,CACzB,OAAO,IAAA,CAAK,YACd,CAEQ,UAAA,EAAmB,CACzB,GAAI,IAAA,CAAK,MAAA,CAAQ,CACf,IAAA,CAAK,SAAA,GACL,MACF,CACA,KAAK,QAAA,GACP,CAEQ,QAAA,EAAiB,CACvB,IAAA,CAAK,MAAA,CAAShR,CAAAA,CAAG,KAAA,CAAO,CAAE,KAAA,CAAO,cAAe,CAAC,CAAA,CACjD,IAAA,CAAK,MAAA,CAAO,aAAa,MAAA,CAAQ,SAAS,CAAA,CAC1C,IAAA,CAAK,MAAA,CAAO,YAAA,CAAa,aAAc,IAAA,CAAK,CAAA,CAAE,YAAY,CAAC,CAAA,CAC3D,KAAK,OAAA,CAAQ,YAAA,CAAa,eAAA,CAAiB,MAAM,CAAA,CAEjD,IAAMiR,EAA+C,CACnD,CAAE,IAAA,CAAM,QAAA,CAAU,KAAA,CAAO,IAAA,CAAK,EAAE,aAAa,CAAE,CAAA,CAC/C,CAAE,IAAA,CAAM,QAAA,CAAU,MAAO,IAAA,CAAK,CAAA,CAAE,aAAa,CAAE,CAAA,CAC/C,CAAE,IAAA,CAAM,SAAA,CAAW,KAAA,CAAO,IAAA,CAAK,CAAA,CAAE,aAAa,CAAE,CAAA,CAChD,CAAE,IAAA,CAAM,YAAA,CAAc,KAAA,CAAO,IAAA,CAAK,EAAE,gBAAgB,CAAE,CACxD,CAAA,CAEA,IAAA,IAAWC,CAAAA,IAAOD,EAAS,CACzB,IAAMvC,EAAO,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA,CAC5CA,CAAAA,CAAK,SAAA,CAAY,CAAA,cAAA,EAAiBwC,CAAAA,CAAI,IAAA,GAAS,KAAK,SAAA,CAAY,yBAAA,CAA4B,EAAE,CAAA,CAAA,CAC9FxC,CAAAA,CAAK,YAAA,CAAa,OAAQ,QAAQ,CAAA,CAClCA,CAAAA,CAAK,YAAA,CAAa,eAAA,CAAiB,MAAA,CAAOwC,EAAI,IAAA,GAAS,IAAA,CAAK,SAAS,CAAC,CAAA,CAElEA,EAAI,IAAA,GAAS,IAAA,CAAK,SAAA,GACpBxC,CAAAA,CAAK,KAAA,CAAM,UAAA,CAAa,KAAK,MAAA,CAAO,WAAA,CACpCA,CAAAA,CAAK,KAAA,CAAM,KAAA,CAAQ,IAAA,CAAK,OAAO,MAAA,CAAA,CAGjCpO,CAAAA,CAAQoO,CAAAA,CAAMwC,CAAAA,CAAI,KAAK,CAAA,CAEvBxC,EAAK,gBAAA,CAAiB,OAAA,CAAU3H,GAAM,CACpCA,CAAAA,CAAE,iBAAgB,CAClB,IAAA,CAAK,SAAA,CAAYmK,CAAAA,CAAI,IAAA,CACrB,IAAA,CAAK,iBAAgB,CACrB,IAAA,CAAK,SAAA,EAAU,CACf,IAAA,CAAK,QAAA,GACP,CAAC,CAAA,CAED,IAAA,CAAK,MAAA,CAAO,WAAA,CAAYxC,CAAI,EAC9B,CAGA,IAAA,CAAK,QAAQ,WAAA,CAAY,IAAA,CAAK,MAAM,CAAA,CAGpC,qBAAA,CAAsB,IAAM,CAC1B,IAAA,CAAK,mBAAA,CAAuB3H,GAAkB,CACxC,IAAA,CAAK,MAAA,EAAU,CAAC,IAAA,CAAK,OAAA,CAAQ,SAASA,CAAAA,CAAE,MAAc,CAAA,EACxD,IAAA,CAAK,SAAA,GAET,EACA,QAAA,CAAS,gBAAA,CAAiB,QAAS,IAAA,CAAK,mBAAA,CAAqB,IAAI,EACnE,CAAC,CAAA,CAGD,IAAA,CAAK,MAAA,CAAO,gBAAA,CAAiB,UAAYA,CAAAA,EAAM,CACzCA,CAAAA,CAAE,GAAA,GAAQ,QAAA,GACZ,IAAA,CAAK,WAAU,CACf,IAAA,CAAK,OAAA,CAAQ,KAAA,EAAM,EAEvB,CAAC,EACH,CAEQ,SAAA,EAAkB,CACpB,IAAA,CAAK,MAAA,GACP,KAAK,MAAA,CAAO,MAAA,EAAO,CACnB,IAAA,CAAK,MAAA,CAAS,IAAA,CAAA,CAEhB,KAAK,OAAA,CAAQ,YAAA,CAAa,eAAA,CAAiB,OAAO,CAAA,CAC9C,IAAA,CAAK,sBACP,QAAA,CAAS,mBAAA,CAAoB,OAAA,CAAS,IAAA,CAAK,mBAAA,CAAqB,IAAI,EACpE,IAAA,CAAK,mBAAA,CAAsB,MAE/B,CAEQ,eAAA,EAAwB,CAC9B,IAAMoK,CAAAA,CAAqC,CACzC,MAAA,CAAQ,IAAA,CAAK,CAAA,CAAE,aAAa,CAAA,CAC5B,MAAA,CAAQ,IAAA,CAAK,CAAA,CAAE,aAAa,CAAA,CAC5B,UAAW,IAAA,CAAK,CAAA,CAAE,aAAa,CAAA,CAC/B,YAAA,CAAc,IAAA,CAAK,EAAE,gBAAgB,CACvC,CAAA,CACMrK,CAAAA,CAAQ,IAAA,CAAK,OAAA,CAAQ,cAAc,oBAAoB,CAAA,CACzDA,CAAAA,EAAOxG,CAAAA,CAAQwG,CAAAA,CAAsBqK,CAAAA,CAAS,KAAK,SAAS,CAAC,EACnE,CAEA,OAAA,EAAgB,CACd,KAAK,SAAA,GACP,CACF,CAAA,CAMaC,EAAAA,CAAW;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACvWjB,EAAA,IAAMC,EAAAA,CAAsB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA,CA6FtBC,EAAAA,CAAN,KAAiB,CAUtB,WAAA,CACmB5N,EACjB4B,CAAAA,CACA,CAFiB,IAAA,CAAA,MAAA,CAAA5B,CAAAA,CAGjB,IAAA,CAAK,CAAA,CAAI4B,CAAAA,CAET,IAAA,CAAK,QAAUtF,CAAAA,CAAG,KAAA,CAAO,CAAE,KAAA,CAAO,cAAe,CAAC,CAAA,CAClD,IAAA,CAAK,QAAQ,YAAA,CAAa,YAAA,CAAc,qBAAqB,CAAA,CAC7D,IAAA,CAAK,OAAA,CAAQ,MAAA,CAAS,IAAA,CAGtB,IAAMuN,CAAAA,CAAMvN,CAAAA,CAAG,KAAA,CAAO,CAAE,KAAA,CAAO,cAAe,CAAC,CAAA,CAGzCuR,EAAWvR,CAAAA,CAAG,KAAA,CAAO,CAAE,KAAA,CAAO,eAAgB,CAAC,CAAA,CAC/CwR,CAAAA,CAAUxR,EAAG,MAAA,CAAQ,CAAE,KAAA,CAAO,cAAe,CAAC,CAAA,CACpDwR,CAAAA,CAAQ,KAAA,CAAM,WAAa,SAAA,CAC3B,IAAA,CAAK,SAAA,CAAYxR,CAAAA,CAAG,OAAQ,CAAE,KAAA,CAAO,gBAAiB,CAAC,EACvDM,CAAAA,CAAQ,IAAA,CAAK,SAAA,CAAW,GAAG,CAAA,CAC3B,IAAMmR,CAAAA,CAAYzR,CAAAA,CAAG,OAAQ,CAAE,KAAA,CAAO,gBAAiB,CAAC,CAAA,CACxDM,CAAAA,CAAQmR,CAAAA,CAAW,IAAA,CAAK,EAAE,YAAY,CAAC,CAAA,CACvCF,CAAAA,CAAS,WAAA,CAAYC,CAAO,CAAA,CAC5BD,CAAAA,CAAS,YAAY,IAAA,CAAK,SAAS,CAAA,CACnCA,CAAAA,CAAS,WAAA,CAAYE,CAAS,CAAA,CAG9B,IAAMC,EAAe1R,CAAAA,CAAG,KAAA,CAAO,CAAE,KAAA,CAAO,eAAgB,CAAC,CAAA,CACnD2R,CAAAA,CAAc3R,EAAG,MAAA,CAAQ,CAAE,KAAA,CAAO,cAAe,CAAC,CAAA,CACxD2R,CAAAA,CAAY,KAAA,CAAM,WAAa,SAAA,CAC/B,IAAA,CAAK,aAAA,CAAgB3R,CAAAA,CAAG,MAAA,CAAQ,CAAE,KAAA,CAAO,gBAAiB,CAAC,CAAA,CAC3DM,CAAAA,CAAQ,IAAA,CAAK,aAAA,CAAe,GAAG,CAAA,CAC/B,IAAMsR,CAAAA,CAAgB5R,CAAAA,CAAG,OAAQ,CAAE,KAAA,CAAO,gBAAiB,CAAC,CAAA,CAC5DM,CAAAA,CAAQsR,CAAAA,CAAe,IAAA,CAAK,EAAE,gBAAgB,CAAC,CAAA,CAC/CF,CAAAA,CAAa,WAAA,CAAYC,CAAW,CAAA,CACpCD,CAAAA,CAAa,YAAY,IAAA,CAAK,aAAa,CAAA,CAC3CA,CAAAA,CAAa,WAAA,CAAYE,CAAa,CAAA,CAGtC,IAAMC,EAAW7R,CAAAA,CAAG,KAAA,CAAO,CAAE,KAAA,CAAO,eAAgB,CAAC,CAAA,CAC/C8R,CAAAA,CAAU9R,EAAG,MAAA,CAAQ,CAAE,KAAA,CAAO,cAAe,CAAC,CAAA,CACpD8R,CAAAA,CAAQ,KAAA,CAAM,WAAa,IAAA,CAAK,MAAA,CAAO,OAAA,CACvC,IAAA,CAAK,SAAA,CAAY9R,CAAAA,CAAG,MAAA,CAAQ,CAAE,MAAO,gBAAiB,CAAC,CAAA,CACvDM,CAAAA,CAAQ,IAAA,CAAK,SAAA,CAAW,GAAG,CAAA,CAC3B,IAAMyR,CAAAA,CAAY/R,CAAAA,CAAG,MAAA,CAAQ,CAAE,MAAO,gBAAiB,CAAC,CAAA,CACxDM,CAAAA,CAAQyR,EAAW,IAAA,CAAK,CAAA,CAAE,YAAY,CAAC,CAAA,CACvCF,CAAAA,CAAS,WAAA,CAAYC,CAAO,EAC5BD,CAAAA,CAAS,WAAA,CAAY,IAAA,CAAK,SAAS,CAAA,CACnCA,CAAAA,CAAS,WAAA,CAAYE,CAAS,EAE9BxE,CAAAA,CAAI,WAAA,CAAYgE,CAAQ,CAAA,CACxBhE,CAAAA,CAAI,WAAA,CAAYmE,CAAY,CAAA,CAC5BnE,EAAI,WAAA,CAAYsE,CAAQ,CAAA,CAGxB,IAAMG,EAAWhS,CAAAA,CAAG,KAAA,CAAO,CAAE,KAAA,CAAO,mBAAoB,CAAC,CAAA,CACnDiS,CAAAA,CAAQjS,CAAAA,CAAG,KAAA,CAAO,CAAE,KAAA,CAAO,yBAA0B,CAAC,CAAA,CAC5D,IAAA,CAAK,YAAA,CAAeA,CAAAA,CAAG,KAAA,CAAO,CAAE,KAAA,CAAO,wBAAyB,CAAC,CAAA,CACjEiS,CAAAA,CAAM,WAAA,CAAY,IAAA,CAAK,YAAY,CAAA,CACnC,IAAA,CAAK,aAAA,CAAgBjS,EAAG,MAAA,CAAQ,CAAE,KAAA,CAAO,yBAA0B,CAAC,CAAA,CACpEM,CAAAA,CAAQ,IAAA,CAAK,aAAA,CAAe,EAAE,CAAA,CAC9B0R,CAAAA,CAAS,WAAA,CAAYC,CAAK,CAAA,CAC1BD,CAAAA,CAAS,WAAA,CAAY,IAAA,CAAK,aAAa,CAAA,CAEvC,IAAA,CAAK,OAAA,CAAQ,WAAA,CAAYzE,CAAG,CAAA,CAC5B,IAAA,CAAK,OAAA,CAAQ,YAAYyE,CAAQ,EACnC,CAhEmB,MAAA,CAVV,OAAA,CAEQ,SAAA,CACA,aAAA,CACA,SAAA,CACA,aACA,aAAA,CACA,CAAA,CAsEjB,MAAA,CAAOlM,CAAAA,CAA+BoM,CAAAA,CAAqB,CAEzD,GAAIA,CAAAA,GAAU,EAAG,CACf,IAAA,CAAK,OAAA,CAAQ,MAAA,CAAS,IAAA,CACtB,MACF,CACA,IAAA,CAAK,QAAQ,MAAA,CAAS,KAAA,CAEtB,IAAIC,CAAAA,CAAY,CAAA,CACZC,CAAAA,CAAgB,CAAA,CAChBC,CAAAA,CAAW,EAEf,IAAA,IAAWpM,CAAAA,IAAMH,CAAAA,CACXG,CAAAA,CAAG,MAAA,GAAW,MAAA,EAAQkM,CAAAA,EAAAA,CACtBlM,CAAAA,CAAG,SAAW,UAAA,EAAYmM,CAAAA,EAAAA,CAC1BnM,CAAAA,CAAG,IAAA,GAAS,OAAOoM,CAAAA,EAAAA,CAGzB/R,CAAAA,CAAQ,IAAA,CAAK,SAAA,CAAW,OAAO6R,CAAS,CAAC,CAAA,CACzC7R,CAAAA,CAAQ,IAAA,CAAK,aAAA,CAAe,MAAA,CAAO8R,CAAa,CAAC,CAAA,CACjD9R,CAAAA,CAAQ,IAAA,CAAK,SAAA,CAAW,MAAA,CAAO+R,CAAQ,CAAC,CAAA,CAGxC,IAAM1J,CAAAA,CAAU7C,CAAAA,CAAU,MAAA,CACpBwM,CAAAA,CAAM3J,CAAAA,CAAU,CAAA,CAAI,IAAA,CAAK,KAAA,CAAOyJ,EAAgBzJ,CAAAA,CAAW,GAAG,CAAA,CAAI,CAAA,CAGxE,sBAAsB,IAAM,CAC1B,IAAA,CAAK,YAAA,CAAa,MAAM,KAAA,CAAQ,CAAA,EAAG2J,CAAG,CAAA,CAAA,EACxC,CAAC,CAAA,CAED,IAAMC,CAAAA,CAAe,KAAK,CAAA,CAAE,gBAAgB,CAAA,CAAE,OAAA,CAAQ,WAAA,CAAa,MAAA,CAAOD,CAAG,CAAC,EAC9EhS,CAAAA,CAAQ,IAAA,CAAK,aAAA,CAAeiS,CAAY,EAC1C,CACF,ECnMO,IAAMC,GAAgB,wVAAA,CAsBtB,SAASC,EAAAA,CAAoBC,CAAAA,CAAoC,CACtE,IAAMC,CAAAA,CAAQD,CAAAA,CAAc,gBAAA,CAA8B,UAAU,CAAA,CACpE,IAAA,IAASE,CAAAA,CAAI,CAAA,CAAGA,CAAAA,CAAID,CAAAA,CAAM,MAAA,CAAQC,CAAAA,EAAAA,CAChC,GAAID,CAAAA,CAAMC,CAAC,CAAA,EAAG,SAAA,CAAU,QAAA,CAAS,kBAAkB,CAAA,CAAG,OAAOA,EAE/D,OAAO,GACT,CAGO,SAASC,EAAAA,CAAiBH,CAAAA,CAA4BnG,CAAAA,CAAqB,CAChF,IAAMoG,CAAAA,CAAQD,CAAAA,CAAc,gBAAA,CAA8B,UAAU,CAAA,CACpE,GAAIC,CAAAA,CAAM,MAAA,GAAW,EAAG,OAGxB,IAAA,IAAWxJ,CAAAA,IAAQwJ,CAAAA,CACjBxJ,CAAAA,CAAK,SAAA,CAAU,MAAA,CAAO,kBAAkB,EAG1C,IAAM2J,CAAAA,CAAU,IAAA,CAAK,GAAA,CAAI,CAAA,CAAG,IAAA,CAAK,GAAA,CAAIvG,CAAAA,CAAOoG,EAAM,MAAA,CAAS,CAAC,CAAC,CAAA,CACvDI,CAAAA,CAASJ,CAAAA,CAAMG,CAAO,CAAA,CACvBC,IACLA,CAAAA,CAAO,SAAA,CAAU,GAAA,CAAI,kBAAkB,EACvCA,CAAAA,CAAO,cAAA,CAAe,CAAE,KAAA,CAAO,UAAW,QAAA,CAAU,QAAS,CAAC,CAAA,CAC9DA,CAAAA,CAAO,KAAA,CAAM,CAAE,aAAA,CAAe,IAAK,CAAC,CAAA,EACtC,CAWA,IAAMC,EAAAA,CAA+B,CACnC,CAAE,IAAA,CAAM,CAAC,GAAA,CAAK,GAAG,CAAA,CAAG,KAAA,CAAO,oBAAqB,CAAA,CAChD,CAAE,IAAA,CAAM,CAAC,GAAG,CAAA,CAAG,KAAA,CAAO,mBAAoB,EAC1C,CAAE,IAAA,CAAM,CAAC,GAAG,EAAG,KAAA,CAAO,kBAAmB,CAAA,CACzC,CAAE,IAAA,CAAM,CAAC,GAAA,CAAK,GAAG,EAAG,KAAA,CAAO,kBAAmB,CAAA,CAC9C,CAAE,IAAA,CAAM,CAAC,GAAG,CAAA,CAAG,MAAO,kBAAmB,CAAA,CACzC,CAAE,IAAA,CAAM,CAAC,GAAG,CAAA,CAAG,KAAA,CAAO,gBAAiB,CAAA,CACvC,CAAE,IAAA,CAAM,CAAC,KAAK,CAAA,CAAG,KAAA,CAAO,iBAAkB,CAC5C,EAMaC,EAAAA,CAA0B;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA,CAyOjCC,EAAAA,CAAgB,gOAAA,CAMTC,EAAAA,CAAN,KAAwB,CAa7B,WAAA,CACEvM,CAAAA,CACAoB,CAAAA,CACiB1C,CAAAA,CACjB,CADiB,IAAA,CAAA,CAAA,CAAAA,CAAAA,CAGjB,IAAA,CAAK,OAAS,IAAI,GAAA,CAAwB,CACxC,CAAC,GAAA,CAAK,IAAM0C,CAAAA,CAAU,UAAA,CAAW,MAAM,CAAC,CAAA,CACxC,CAAC,GAAA,CAAK,IAAMA,CAAAA,CAAU,UAAA,CAAW,IAAI,CAAC,CAAA,CACtC,CAAC,GAAA,CAAK,IAAMA,CAAAA,CAAU,SAAA,EAAW,CAAA,CACjC,CAAC,GAAA,CAAK,IAAMA,CAAAA,CAAU,QAAA,EAAU,CAAA,CAChC,CAAC,GAAA,CAAK,IAAMA,CAAAA,CAAU,aAAA,EAAe,CAAA,CACrC,CAAC,GAAA,CAAK,IAAMA,CAAAA,CAAU,eAAe,CAAA,CACrC,CAAC,GAAA,CAAK,IAAMA,CAAAA,CAAU,cAAA,EAAgB,EACtC,CAAC,GAAA,CAAK,IAAM,IAAA,CAAK,UAAA,EAAY,CAC/B,CAAC,EAGD,IAAA,CAAK,WAAA,CAAc,IAAA,CAAK,YAAA,EAAa,CACrC,IAAA,CAAK,UAAA,CAAa,IAAA,CAAK,iBAAgB,CAGvC,IAAA,CAAK,YAAA,CAAgBjB,CAAAA,EAAqB,IAAA,CAAK,aAAA,CAAcA,CAAC,EAChE,CApBmB,CAAA,CAdV,WAAA,CAEA,UAAA,CAEQ,MAAA,CACA,YAAA,CACT,UAAA,CAA8C,IAAA,CAC9C,OAAA,CAAU,MACV,WAAA,CAAc,KAAA,CACd,SAAA,CAAY,KAAA,CAgCpB,MAAA,CAAOqM,CAAAA,CAAuC,CAC5C,GAAI,KAAK,SAAA,EAAa,IAAA,CAAK,OAAA,CAAS,OAChCA,CAAAA,GAAM,IAAA,CAAK,UAAA,CAAaA,CAAAA,CAAAA,CAAAA,CACb,KAAK,UAAA,EAAc,QAAA,EAC3B,gBAAA,CAAiB,SAAA,CAAW,IAAA,CAAK,YAA6B,CAAA,CACrE,IAAA,CAAK,QAAU,KACjB,CAGA,OAAA,EAAgB,CACd,GAAI,CAAC,IAAA,CAAK,OAAA,CAAS,QACJ,IAAA,CAAK,UAAA,EAAc,QAAA,EAC3B,mBAAA,CAAoB,SAAA,CAAW,IAAA,CAAK,YAA6B,CAAA,CACxE,KAAK,OAAA,CAAU,KAAA,CAEX,IAAA,CAAK,WAAA,EAAa,IAAA,CAAK,QAAA,GAC7B,CAGA,YAAmB,CACb,IAAA,CAAK,WAAA,CACP,IAAA,CAAK,QAAA,EAAS,CAEd,IAAA,CAAK,QAAA,GAET,CAGA,OAAA,EAAgB,CACV,IAAA,CAAK,SAAA,GACT,IAAA,CAAK,OAAA,EAAQ,CACb,KAAK,WAAA,CAAY,MAAA,EAAO,CACxB,IAAA,CAAK,UAAA,CAAW,MAAA,EAAO,CACvB,IAAA,CAAK,UAAY,IAAA,EACnB,CAMQ,aAAA,CAAc,CAAA,CAAwB,CAE5C,GAAI,CAAA,CAAE,GAAA,GAAQ,SAAU,CAClB,IAAA,CAAK,WAAA,GACP,CAAA,CAAE,cAAA,EAAe,CACjB,CAAA,CAAE,eAAA,GACF,IAAA,CAAK,QAAA,EAAS,CAAA,CAEhB,MACF,CAGA,GAAI,IAAA,CAAK,WAAA,CAAa,OAGtB,IAAMC,CAAAA,CAAS,CAAA,CAAE,YAAA,EAAa,CAAE,CAAC,CAAA,CACjC,GAAIA,EAAQ,CACV,IAAMpT,CAAAA,CAAMoT,CAAAA,CAAO,OAAA,EAAS,WAAA,EAAY,CAExC,GADIpT,IAAQ,OAAA,EAAWA,CAAAA,GAAQ,UAAA,EAAcA,CAAAA,GAAQ,QAAA,EACjDoT,CAAAA,CAAO,iBAAA,CAAmB,MAChC,CAGA,GAAI,CAAA,CAAE,OAAA,EAAW,CAAA,CAAE,MAAA,EAAU,CAAA,CAAE,OAAA,CAAS,OAExC,IAAMC,CAAAA,CAAU,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,CAAA,CAAE,GAAG,CAAA,CACjCA,CAAAA,GACF,EAAE,cAAA,EAAe,CACjB,CAAA,CAAE,eAAA,EAAgB,CAClBA,CAAAA,EAAQ,EAEZ,CAMQ,UAAiB,CACvB,IAAA,CAAK,WAAA,CAAc,IAAA,CACnB,IAAA,CAAK,WAAA,CAAY,SAAA,CAAU,GAAA,CAAI,+BAA+B,CAAA,CAG7C,IAAA,CAAK,WAAA,CAAY,aAAA,CAAiC,qBAAqB,CAAA,EAC9E,KAAA,GACZ,CAEQ,QAAA,EAAiB,CACvB,IAAA,CAAK,WAAA,CAAc,KAAA,CACnB,IAAA,CAAK,WAAA,CAAY,SAAA,CAAU,OAAO,+BAA+B,EACnE,CAMQ,YAAA,EAA4B,CAClC,IAAMC,CAAAA,CAAUvT,CAAAA,CAAG,MAAO,CAAE,KAAA,CAAO,sBAAuB,CAAC,CAAA,CAC3DuT,CAAAA,CAAQ,YAAA,CAAa,MAAA,CAAQ,QAAQ,CAAA,CACrCA,CAAAA,CAAQ,YAAA,CAAa,YAAA,CAAc,MAAM,CAAA,CACzCA,CAAAA,CAAQ,YAAA,CAAa,aAAc,IAAA,CAAK,CAAA,CAAE,iBAAiB,CAAC,CAAA,CAG5DA,CAAAA,CAAQ,gBAAA,CAAiB,OAAA,CAAUxM,GAAM,CACnCA,CAAAA,CAAE,MAAA,GAAWwM,CAAAA,EAAS,IAAA,CAAK,QAAA,GACjC,CAAC,EAED,IAAMpK,CAAAA,CAAOnJ,CAAAA,CAAG,KAAA,CAAO,CAAE,KAAA,CAAO,mBAAoB,CAAC,EAG/CqL,CAAAA,CAAQrL,CAAAA,CAAG,KAAA,CAAO,CAAE,KAAA,CAAO,oBAAqB,CAAC,CAAA,CACvDqL,EAAM,WAAA,CAAYzL,CAAAA,CAAS4S,EAAa,CAAC,CAAA,CACzC,IAAMgB,CAAAA,CAAYxT,CAAAA,CAAG,MAAM,CAAA,CAC3BM,CAAAA,CAAQkT,CAAAA,CAAW,IAAA,CAAK,CAAA,CAAE,iBAAiB,CAAC,CAAA,CAC5CnI,EAAM,WAAA,CAAYmI,CAAS,CAAA,CAC3BrK,CAAAA,CAAK,WAAA,CAAYkC,CAAK,CAAA,CAGtB,IAAMoI,EAAW,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA,CAChDA,CAAAA,CAAS,SAAA,CAAY,oBAAA,CACrBA,CAAAA,CAAS,aAAa,YAAA,CAAc,IAAA,CAAK,CAAA,CAAE,iBAAiB,CAAC,CAAA,CAC7DA,CAAAA,CAAS,WAAA,CAAY7T,EAASsT,EAAa,CAAC,CAAA,CAC5CO,CAAAA,CAAS,gBAAA,CAAiB,OAAA,CAAS,IAAM,IAAA,CAAK,UAAU,CAAA,CACxDtK,CAAAA,CAAK,WAAA,CAAYsK,CAAQ,CAAA,CAGzB,IAAMC,CAAAA,CAAO1T,EAAG,KAAA,CAAO,CAAE,KAAA,CAAO,mBAAoB,CAAC,CAAA,CAErD,IAAA,IAAW2T,CAAAA,IAAOX,GAAe,CAC/B,IAAMzF,CAAAA,CAAMvN,CAAAA,CAAG,KAAA,CAAO,CAAE,KAAA,CAAO,kBAAmB,CAAC,CAAA,CAE7C4T,CAAAA,CAAW5T,CAAAA,CAAG,KAAA,CAAO,CAAE,KAAA,CAAO,mBAAoB,CAAC,EACzD2T,CAAAA,CAAI,IAAA,CAAK,OAAA,CAAQ,CAACvT,CAAAA,CAAKwS,CAAAA,GAAM,CAC3B,GAAIA,EAAI,CAAA,CAAG,CACT,IAAMiB,CAAAA,CAAM7T,CAAAA,CAAG,MAAA,CAAQ,CAAE,KAAA,CAAO,wBAAyB,CAAC,CAAA,CAC1DM,CAAAA,CAAQuT,CAAAA,CAAK,GAAG,CAAA,CAChBD,CAAAA,CAAS,WAAA,CAAYC,CAAG,EAC1B,CACA,IAAMC,CAAAA,CAAM9T,CAAAA,CAAG,MAAA,CAAQ,CAAE,KAAA,CAAO,QAAS,CAAC,CAAA,CAC1CM,CAAAA,CAAQwT,CAAAA,CAAK1T,CAAG,CAAA,CAChBwT,CAAAA,CAAS,WAAA,CAAYE,CAAG,EAC1B,CAAC,CAAA,CAED,IAAMC,CAAAA,CAAO/T,CAAAA,CAAG,MAAA,CAAQ,CAAE,MAAO,mBAAoB,CAAC,CAAA,CACtDM,CAAAA,CAAQyT,CAAAA,CAAM,IAAA,CAAK,CAAA,CAAEJ,CAAAA,CAAI,KAAK,CAAC,CAAA,CAE/BpG,CAAAA,CAAI,WAAA,CAAYqG,CAAQ,CAAA,CACxBrG,CAAAA,CAAI,WAAA,CAAYwG,CAAI,CAAA,CACpBL,CAAAA,CAAK,WAAA,CAAYnG,CAAG,EACtB,CAEA,OAAApE,CAAAA,CAAK,YAAYuK,CAAI,CAAA,CACrBH,CAAAA,CAAQ,WAAA,CAAYpK,CAAI,CAAA,CAEjBoK,CACT,CAEQ,iBAAqC,CAC3C,IAAM9S,CAAAA,CAAM,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA,CAC3C,OAAAA,EAAI,SAAA,CAAY,mBAAA,CAChBA,CAAAA,CAAI,YAAA,CAAa,YAAA,CAAc,IAAA,CAAK,CAAA,CAAE,gBAAgB,CAAC,CAAA,CACvDH,CAAAA,CAAQG,CAAAA,CAAK,GAAG,CAAA,CAChBA,CAAAA,CAAI,gBAAA,CAAiB,OAAA,CAAUsG,GAAM,CACnCA,CAAAA,CAAE,eAAA,EAAgB,CAClB,KAAK,UAAA,GACP,CAAC,CAAA,CACMtG,CACT,CACF","file":"chunk-EF2HNCBX.js","sourcesContent":["/**\n * Safe DOM creation utilities.\n * All user content is set via textContent (never innerHTML).\n * SVG icons use a DOMParser for trusted static strings.\n */\n\n/**\n * Parse a trusted SVG string into an SVGElement.\n * Only use with hardcoded icon constants — never with user input.\n * Uses createContextualFragment for native document-context parsing\n * (DOMParser creates nodes in a foreign document that don't render in Shadow DOM).\n */\nexport function parseSvg(svgString: string): SVGSVGElement {\n const range = document.createRange();\n const fragment = range.createContextualFragment(svgString);\n const svg = fragment.firstElementChild;\n if (!svg || svg.nodeName.toLowerCase() !== \"svg\") {\n throw new Error(\"[siteping] Invalid SVG string\");\n }\n // Safety: strip any event handlers in case of accidental misuse\n for (const attr of [...svg.attributes]) {\n if (attr.name.startsWith(\"on\")) svg.removeAttribute(attr.name);\n }\n // Also strip from all descendants\n for (const el of svg.querySelectorAll(\"*\")) {\n for (const attr of [...el.attributes]) {\n if (attr.name.startsWith(\"on\")) el.removeAttribute(attr.name);\n }\n }\n return svg as SVGSVGElement;\n}\n\n/** Create an element with optional class and style */\nexport function el(tag: string, attrs?: Record<string, string>): HTMLElement {\n const element = document.createElement(tag);\n if (attrs) {\n for (const [key, value] of Object.entries(attrs)) {\n if (key === \"class\") {\n element.className = value;\n } else if (key === \"style\") {\n element.style.cssText = value;\n } else {\n element.setAttribute(key, value);\n }\n }\n }\n return element;\n}\n\n/** Set text content safely (no HTML injection possible) */\nexport function setText(element: HTMLElement | SVGElement, text: string): void {\n element.textContent = text;\n}\n\n/**\n * Replace a button's children with a small spinner and disable it.\n * Returns a `restore` callback that swaps the original content back and\n * re-enables the button. Used by every async button (delete, resolve, …)\n * to surface in-flight state without owning per-button state itself.\n *\n * Lives in dom-utils rather than panel-internal because both Panel and\n * BulkActions need identical behaviour.\n */\nexport function setButtonLoading(btn: HTMLButtonElement): () => void {\n const snapshot = Array.from(btn.childNodes).map((n) => n.cloneNode(true));\n btn.disabled = true;\n btn.replaceChildren(el(\"div\", { class: \"sp-spinner sp-spinner--sm\" }));\n return () => {\n btn.replaceChildren(...snapshot);\n btn.disabled = false;\n };\n}\n\n/** Format a relative date string using Intl.RelativeTimeFormat for locale support */\nexport function formatRelativeDate(isoString: string, locale = \"en\"): string {\n const diff = Date.now() - new Date(isoString).getTime();\n const seconds = Math.floor(diff / 1000);\n\n if (seconds < 60) {\n return new Intl.RelativeTimeFormat(locale, { numeric: \"auto\" }).format(0, \"second\");\n }\n\n const rtf = new Intl.RelativeTimeFormat(locale, { numeric: \"always\", style: \"narrow\" });\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) return rtf.format(-minutes, \"minute\");\n\n const hours = Math.floor(minutes / 60);\n if (hours < 24) return rtf.format(-hours, \"hour\");\n\n const days = Math.floor(hours / 24);\n if (days < 7) return rtf.format(-days, \"day\");\n\n return new Date(isoString).toLocaleDateString(locale);\n}\n","/** SVG icon strings for the widget UI. Kept as template strings to avoid DOM parsing overhead. */\n\nexport const ICON_SITEPING = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"/><circle cx=\"12\" cy=\"10\" r=\"1\" fill=\"currentColor\" stroke=\"none\"/><circle cx=\"8\" cy=\"10\" r=\"1\" fill=\"currentColor\" stroke=\"none\"/><circle cx=\"16\" cy=\"10\" r=\"1\" fill=\"currentColor\" stroke=\"none\"/></svg>`;\n\nexport const ICON_CHAT = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"/></svg>`;\n\nexport const ICON_ANNOTATE = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/><path d=\"M3 9h18\"/><path d=\"M9 3v18\"/></svg>`;\n\nexport const ICON_EYE = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z\"/><circle cx=\"12\" cy=\"12\" r=\"3\"/></svg>`;\n\nexport const ICON_EYE_OFF = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94\"/><path d=\"M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19\"/><line x1=\"1\" y1=\"1\" x2=\"23\" y2=\"23\"/></svg>`;\n\nexport const ICON_CLOSE = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/></svg>`;\n\nexport const ICON_SEARCH = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><circle cx=\"11\" cy=\"11\" r=\"8\"/><line x1=\"21\" y1=\"21\" x2=\"16.65\" y2=\"16.65\"/></svg>`;\n\nexport const ICON_CHECK = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><polyline points=\"20 6 9 17 4 12\"/></svg>`;\n\nexport const ICON_QUESTION = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3\"/><line x1=\"12\" y1=\"17\" x2=\"12.01\" y2=\"17\"/></svg>`;\n\nexport const ICON_CHANGE = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7\"/><path d=\"M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z\"/></svg>`;\n\nexport const ICON_BUG = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><rect x=\"8\" y=\"6\" width=\"8\" height=\"14\" rx=\"4\"/><path d=\"M19 9h2\"/><path d=\"M3 9h2\"/><path d=\"M19 13h2\"/><path d=\"M3 13h2\"/><path d=\"M19 17h2\"/><path d=\"M3 17h2\"/><path d=\"M10 2h4\"/></svg>`;\n\nexport const ICON_OTHER = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"1\"/><circle cx=\"19\" cy=\"12\" r=\"1\"/><circle cx=\"5\" cy=\"12\" r=\"1\"/></svg>`;\n\nexport const ICON_LAYERS = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><polygon points=\"12 2 2 7 12 12 22 7 12 2\"/><polyline points=\"2 17 12 22 22 17\"/><polyline points=\"2 12 12 17 22 12\"/></svg>`;\n\nexport const ICON_DOT_OPEN = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"9\"/><circle cx=\"12\" cy=\"12\" r=\"3\" fill=\"currentColor\" stroke=\"none\"/></svg>`;\n\nexport const ICON_CHEVRON_DOWN = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><polyline points=\"6 9 12 15 18 9\"/></svg>`;\n\nexport const ICON_UNDO = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><polyline points=\"1 4 1 10 7 10\"/><path d=\"M3.51 15a9 9 0 1 0 2.13-9.36L1 10\"/></svg>`;\n\nexport const ICON_TRASH = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><polyline points=\"3 6 5 6 21 6\"/><path d=\"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\"/><line x1=\"10\" y1=\"11\" x2=\"10\" y2=\"17\"/><line x1=\"14\" y1=\"11\" x2=\"14\" y2=\"17\"/></svg>`;\n","/** Color palette and glassmorphism tokens derived from the accent color */\nexport interface ThemeColors {\n accent: string;\n accentLight: string;\n accentDark: string;\n accentGlow: string;\n accentGradient: string;\n bg: string;\n bgHover: string;\n text: string;\n textSecondary: string;\n textTertiary: string;\n border: string;\n shadow: string;\n // Glass tokens\n glassBg: string;\n glassBgHeavy: string;\n glassBorder: string;\n glassBorderSubtle: string;\n // Feedback type colors\n typeQuestion: string;\n typeChange: string;\n typeBug: string;\n typeOther: string;\n // Soft type backgrounds (pastel)\n typeQuestionBg: string;\n typeChangeBg: string;\n typeBugBg: string;\n typeOtherBg: string;\n // Status filter colors\n statusOpen: string;\n statusOpenBg: string;\n statusResolved: string;\n statusResolvedBg: string;\n}\n\nconst DEFAULT_ACCENT = \"#0066ff\";\nconst HEX6_RE = /^#[0-9a-fA-F]{6}$/;\nconst HEX3_RE = /^#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])$/;\nconst HEX8_RE = /^#[0-9a-fA-F]{8}$/;\n\n/**\n * Normalize an accent color to a 6-digit hex string.\n *\n * **Only hex formats are accepted:**\n * - `#RGB` (3-digit shorthand, expanded to 6-digit)\n * - `#RRGGBB` (standard 6-digit)\n * - `#RRGGBBAA` (8-digit with alpha, alpha is stripped)\n *\n * Any other CSS color format (named colors like `\"red\"`, `hsl()`, `rgb()`,\n * `oklch()`, etc.) is **not** supported and will fall back to the default\n * accent color with a console warning.\n */\nfunction normalizeHex(raw: string): string {\n if (HEX6_RE.test(raw)) return raw;\n const short = HEX3_RE.test(raw) ? raw.match(HEX3_RE) : null;\n if (short) return `#${short[1]}${short[1]}${short[2]}${short[2]}${short[3]}${short[3]}`;\n if (HEX8_RE.test(raw)) return raw.slice(0, 7);\n\n console.warn(\n `[siteping] Invalid accentColor \"${raw}\" — only hex colors (#RGB, #RRGGBB, #RRGGBBAA) are supported. Using default.`,\n );\n return DEFAULT_ACCENT;\n}\n\n/** Darken a hex color by a percentage (0-1) */\nfunction darkenHex(hex: string, amount: number): string {\n const r = Math.max(0, Math.round(parseInt(hex.slice(1, 3), 16) * (1 - amount)));\n const g = Math.max(0, Math.round(parseInt(hex.slice(3, 5), 16) * (1 - amount)));\n const b = Math.max(0, Math.round(parseInt(hex.slice(5, 7), 16) * (1 - amount)));\n return `#${r.toString(16).padStart(2, \"0\")}${g.toString(16).padStart(2, \"0\")}${b.toString(16).padStart(2, \"0\")}`;\n}\n\n/** Detect if user prefers dark mode via media query */\nfunction prefersDark(): boolean {\n if (typeof window === \"undefined\") return false;\n return window.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n}\n\n/** Resolve 'auto' theme to 'light' or 'dark' based on system preference */\nexport function resolveTheme(theme?: \"light\" | \"dark\" | \"auto\"): \"light\" | \"dark\" {\n if (theme === \"dark\") return \"dark\";\n if (theme === \"auto\") return prefersDark() ? \"dark\" : \"light\";\n return \"light\";\n}\n\nexport function buildThemeColors(accent: string = DEFAULT_ACCENT, theme?: \"light\" | \"dark\" | \"auto\"): ThemeColors {\n const hex = normalizeHex(accent);\n const dark = darkenHex(hex, 0.15);\n const resolved = resolveTheme(theme);\n\n if (resolved === \"dark\") {\n return {\n accent: hex,\n accentLight: hex + \"22\", // slightly more visible on dark bg\n accentDark: dark,\n accentGlow: hex + \"44\",\n accentGradient: `linear-gradient(135deg, ${hex}, ${dark})`,\n bg: \"#0f172a\",\n bgHover: \"#1e293b\",\n text: \"#f1f5f9\",\n textSecondary: \"#94a3b8\",\n textTertiary: \"#64748b\",\n border: \"#334155\",\n shadow: \"rgba(0, 0, 0, 0.3)\",\n // Glass tokens — dark frosted glass\n glassBg: \"rgba(15, 23, 42, 0.78)\",\n glassBgHeavy: \"rgba(15, 23, 42, 0.88)\",\n glassBorder: \"rgba(51, 65, 85, 0.5)\",\n glassBorderSubtle: \"rgba(51, 65, 85, 0.3)\",\n // Type colors stay vibrant on dark\n typeQuestion: \"#60a5fa\",\n typeChange: \"#fbbf24\",\n typeBug: \"#f87171\",\n typeOther: \"#94a3b8\",\n // Dark pastel backgrounds\n typeQuestionBg: \"rgba(59, 130, 246, 0.15)\",\n typeChangeBg: \"rgba(245, 158, 11, 0.15)\",\n typeBugBg: \"rgba(239, 68, 68, 0.15)\",\n typeOtherBg: \"rgba(100, 116, 139, 0.15)\",\n // Status colors — vivid green / cool gray on dark\n statusOpen: \"#4ade80\",\n statusOpenBg: \"rgba(74, 222, 128, 0.15)\",\n statusResolved: \"#94a3b8\",\n statusResolvedBg: \"rgba(148, 163, 184, 0.15)\",\n };\n }\n\n return {\n accent: hex,\n accentLight: hex + \"14\", // 8% opacity\n accentDark: dark,\n accentGlow: hex + \"33\", // 20% opacity\n accentGradient: `linear-gradient(135deg, ${hex}, ${dark})`,\n bg: \"#ffffff\",\n bgHover: \"#f8f9fb\",\n text: \"#0f172a\",\n textSecondary: \"#475569\",\n textTertiary: \"#64748b\",\n border: \"#e2e8f0\",\n shadow: \"rgba(0, 0, 0, 0.06)\",\n // Glass tokens\n glassBg: \"rgba(255, 255, 255, 0.72)\",\n glassBgHeavy: \"rgba(255, 255, 255, 0.85)\",\n glassBorder: \"rgba(255, 255, 255, 0.35)\",\n glassBorderSubtle: \"rgba(255, 255, 255, 0.18)\",\n // Vibrant type colors\n typeQuestion: \"#3b82f6\",\n typeChange: \"#b45309\",\n typeBug: \"#ef4444\",\n typeOther: \"#64748b\",\n // Pastel backgrounds\n typeQuestionBg: \"#eff6ff\",\n typeChangeBg: \"#fffbeb\",\n typeBugBg: \"#fef2f2\",\n typeOtherBg: \"#f8fafc\",\n // Status colors — saturated green / cool gray on light\n statusOpen: \"#16a34a\",\n statusOpenBg: \"#f0fdf4\",\n statusResolved: \"#64748b\",\n statusResolvedBg: \"#f1f5f9\",\n };\n}\n\nexport function getTypeColor(type: string, colors: ThemeColors): string {\n switch (type) {\n case \"question\":\n return colors.typeQuestion;\n case \"change\":\n return colors.typeChange;\n case \"bug\":\n return colors.typeBug;\n default:\n return colors.typeOther;\n }\n}\n\nexport function getTypeBgColor(type: string, colors: ThemeColors): string {\n switch (type) {\n case \"question\":\n return colors.typeQuestionBg;\n case \"change\":\n return colors.typeChangeBg;\n case \"bug\":\n return colors.typeBugBg;\n default:\n return colors.typeOtherBg;\n }\n}\n\nexport function cssVariables(colors: ThemeColors): string {\n return `\n --sp-accent: ${colors.accent};\n --sp-accent-light: ${colors.accentLight};\n --sp-accent-dark: ${colors.accentDark};\n --sp-accent-glow: ${colors.accentGlow};\n --sp-accent-gradient: ${colors.accentGradient};\n --sp-bg: ${colors.bg};\n --sp-bg-hover: ${colors.bgHover};\n --sp-text: ${colors.text};\n --sp-text-secondary: ${colors.textSecondary};\n --sp-text-tertiary: ${colors.textTertiary};\n --sp-border: ${colors.border};\n --sp-shadow: ${colors.shadow};\n --sp-glass-bg: ${colors.glassBg};\n --sp-glass-bg-heavy: ${colors.glassBgHeavy};\n --sp-glass-border: ${colors.glassBorder};\n --sp-glass-border-subtle: ${colors.glassBorderSubtle};\n --sp-type-question: ${colors.typeQuestion};\n --sp-type-change: ${colors.typeChange};\n --sp-type-bug: ${colors.typeBug};\n --sp-type-other: ${colors.typeOther};\n --sp-type-question-bg: ${colors.typeQuestionBg};\n --sp-type-change-bg: ${colors.typeChangeBg};\n --sp-type-bug-bg: ${colors.typeBugBg};\n --sp-type-other-bg: ${colors.typeOtherBg};\n --sp-radius: 12px;\n --sp-radius-lg: 16px;\n --sp-radius-xl: 20px;\n --sp-radius-full: 9999px;\n --sp-blur: 20px;\n --sp-blur-heavy: 32px;\n --sp-shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.04);\n --sp-shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.05), 0 1px 2px rgba(0, 0, 0, 0.04);\n --sp-shadow-md: 0 4px 16px rgba(0, 0, 0, 0.08), 0 2px 4px rgba(0, 0, 0, 0.04);\n --sp-shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.1), 0 4px 8px rgba(0, 0, 0, 0.04);\n --sp-shadow-xl: 0 16px 48px rgba(0, 0, 0, 0.12), 0 8px 16px rgba(0, 0, 0, 0.06);\n --sp-font: \"Inter\", system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif;\n `;\n}\n","/**\n * Typed error hierarchy for Siteping client/server boundaries.\n *\n * Consumers can `instanceof`-check or read `code` / `retryable` instead of\n * pattern-matching error messages. Designed to be additive on top of the\n * existing store errors (`StoreNotFoundError`, `StoreDuplicateError`) which\n * remain the canonical signals for server-side store implementations.\n *\n * Usage on the widget side (api-client.ts):\n * - fetch failures / aborts / timeouts → `SitepingNetworkError` (retryable)\n * - HTTP 4xx (except 401/403) → `SitepingValidationError` (not retryable)\n * - HTTP 401 / 403 → `SitepingAuthError` (not retryable)\n * - everything else → `SitepingError` generic\n *\n * `retryable` is meta information surfaced to host apps that want to wire\n * their own retry/queue/backoff strategy — the widget already retries\n * network failures via its built-in retry queue.\n */\n\n/**\n * Discriminant string carried by every `SitepingError`. Subclasses pin a\n * literal value; the base class accepts a wider string so userland can\n * extend the hierarchy without colliding with built-ins.\n */\nexport type SitepingErrorCode = \"NETWORK\" | \"VALIDATION\" | \"AUTH\" | \"SERVER\" | (string & {});\n\nexport class SitepingError<TCode extends SitepingErrorCode = SitepingErrorCode> extends Error {\n readonly code: TCode;\n readonly retryable: boolean;\n\n constructor(message: string, code: TCode, retryable: boolean) {\n super(message);\n this.code = code;\n this.retryable = retryable;\n this.name = \"SitepingError\";\n }\n}\n\n/** Network-level failure: connection refused, DNS, CORS, timeout, abort. Retryable. */\nexport class SitepingNetworkError extends SitepingError<\"NETWORK\"> {\n constructor(message: string) {\n super(message, \"NETWORK\", true);\n this.name = \"SitepingNetworkError\";\n }\n}\n\n/** Server rejected the request (4xx, not auth). Validation problem on the client side. */\nexport class SitepingValidationError extends SitepingError<\"VALIDATION\"> {\n constructor(message: string) {\n super(message, \"VALIDATION\", false);\n this.name = \"SitepingValidationError\";\n }\n}\n\n/** Server rejected auth (401 or 403). Not retryable without fresh credentials. */\nexport class SitepingAuthError extends SitepingError<\"AUTH\"> {\n constructor(message: string) {\n super(message, \"AUTH\", false);\n this.name = \"SitepingAuthError\";\n }\n}\n","/**\n * General-purpose TypeScript utility types used across `@siteping/*`.\n *\n * These are kept dependency-free and re-exported from the package entry\n * so adapters and integrators can rely on the same primitives the core\n * uses internally.\n */\n\n/**\n * Force TypeScript to expand a computed type into a flat object literal in\n * tooltips and error messages. Purely cosmetic — same structural type, just\n * easier to read.\n *\n * @example\n * type Raw = Omit<FeedbackRecord, \"annotations\"> & { annotations: number };\n * type Pretty = Prettify<Raw>; // displayed as a flat object\n */\nexport type Prettify<T> = { [K in keyof T]: T[K] } & {};\n\n/**\n * Branded primitive — opaque alias safer than a raw `string` for IDs and\n * other opaque tokens. Cast through `as` at the construction boundary.\n *\n * @example\n * type FeedbackId = Brand<string, \"FeedbackId\">;\n * const id = \"abc\" as FeedbackId;\n */\nexport type Brand<T, B extends string> = T & { readonly __brand: B };\n\n/**\n * Tuple type for arrays guaranteed to contain at least one element. Useful\n * for invariants enforced at the type system level.\n */\nexport type NonEmptyArray<T> = readonly [T, ...T[]];\n\n/**\n * Returns `Y` when `A` is exactly assignable to `B` and vice-versa,\n * otherwise `N`. Powers compile-time equality assertions.\n */\nexport type IfEquals<A, B, Y = true, N = false> =\n (<T>() => T extends A ? 1 : 2) extends <T>() => T extends B ? 1 : 2 ? Y : N;\n\n/**\n * Compile-time exact-type guard — throws a type error when `Actual` and\n * `Expected` drift apart. Use inside dead-code `void` statements to lock\n * inferred types against manually-written interfaces.\n */\nexport type AssertEqual<Actual, Expected> = IfEquals<Actual, Expected, true, never>;\n\n/**\n * Extract literal keys whose values match the predicate type `V`.\n * Useful to derive enums or maps from a config record.\n */\nexport type KeysOfType<T, V> = { [K in keyof T]-?: T[K] extends V ? K : never }[keyof T];\n\n/**\n * Mark every property of `T` as required AND non-nullable. Stricter than\n * `Required<T>` because it also strips `null`.\n */\nexport type RequiredNonNull<T> = { [K in keyof T]-?: NonNullable<T[K]> };\n\n/**\n * Replace properties of `T` whose names are in `K` with the shape `R[K]`.\n * Preserves the rest. Cheaper than chaining `Omit` + `Pick`.\n */\nexport type Replace<T, K extends keyof T, R extends { [P in K]: unknown }> = Prettify<Omit<T, K> & R>;\n\n/**\n * Deep readonly — recurses through arrays and plain objects, leaves\n * primitives, classes, and built-ins (Date, Map, Set, …) untouched.\n */\nexport type DeepReadonly<T> = T extends (infer U)[]\n ? ReadonlyArray<DeepReadonly<U>>\n : T extends ReadonlyArray<infer U>\n ? ReadonlyArray<DeepReadonly<U>>\n : T extends object\n ? T extends (...args: never[]) => unknown\n ? T\n : T extends Date | RegExp | Map<unknown, unknown> | Set<unknown> | Promise<unknown>\n ? T\n : { readonly [K in keyof T]: DeepReadonly<T[K]> }\n : T;\n\n/**\n * Type guard that narrows `value` to a non-null `Record<PropertyKey, unknown>`.\n * Useful when validating arbitrary inputs before reading fields.\n */\nexport function isRecord(value: unknown): value is Record<PropertyKey, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\n/**\n * Returns true when `value` is an object that exposes the requested key.\n * Type-narrows `value` so the property can be accessed without further\n * casting — a strictly typed replacement for `\"k\" in obj`.\n *\n * Named after the standardised `Object.hasOwn` helper rather than the\n * legacy `Object.prototype.hasOwnProperty`, which the linter forbids\n * shadowing.\n */\nexport function hasOwn<K extends PropertyKey>(value: unknown, key: K): value is Record<K, unknown> {\n return isRecord(value) && key in value;\n}\n","import { hasOwn, type Prettify } from \"./type-utils.js\";\n\n// ---------------------------------------------------------------------------\n// Config\n// ---------------------------------------------------------------------------\n\n/** FAB anchor — bottom-corner placement supported by the widget. */\nexport type SitepingPosition = \"bottom-right\" | \"bottom-left\";\n\n/** Visual theme — `auto` resolves to `light` or `dark` via system preference. */\nexport type SitepingTheme = \"light\" | \"dark\" | \"auto\";\n\n/** Built-in UI locales shipped with the widget. */\nexport const BUILTIN_LOCALES = [\"en\", \"fr\", \"de\", \"es\", \"it\", \"pt\", \"ru\"] as const;\nexport type BuiltinLocale = (typeof BUILTIN_LOCALES)[number];\n\n/**\n * Locale identifier accepted by the widget. Built-in locales are kept as\n * literal strings so editors auto-complete them, but arbitrary BCP-47 tags\n * are also accepted (custom dictionaries registered via `registerLocale`).\n */\nexport type SitepingLocale = BuiltinLocale | (string & {});\n\n/** Reasons reported through `SitepingConfig.onSkip`. */\nexport type SitepingSkipReason = \"production\" | \"mobile\";\n\n/** Per-channel + per-buffer-size diagnostics configuration. */\nexport interface DiagnosticsCaptureOptions {\n console?: boolean;\n network?: boolean;\n maxConsoleEntries?: number;\n maxNetworkEntries?: number;\n}\n\n/** Identity payload supplied by the host application — bypasses the modal. */\nexport interface SitepingIdentity {\n name: string;\n email: string;\n}\n\n/** Deep-link configuration — controls how a feedback id is read from the URL. */\nexport interface SitepingDeepLinkOptions {\n /** Query parameter name carrying the feedback id. Defaults to `\"siteping\"`. */\n param?: string;\n}\n\n/** Configuration options for the Siteping widget. */\nexport interface SitepingConfig {\n /** HTTP endpoint that receives feedbacks (e.g. '/api/siteping'). Required unless `store` is provided. */\n endpoint?: string | undefined;\n /** Required — project identifier used to scope feedbacks */\n projectName: string;\n /** Direct store for client-side mode. When set, bypasses HTTP and uses the store directly in the browser. */\n store?: SitepingStore | undefined;\n /** FAB position — defaults to 'bottom-right' */\n position?: SitepingPosition;\n /** Accent color for the widget UI — defaults to '#0066ff' */\n accentColor?: string;\n /** Show the widget even in production — defaults to false */\n forceShow?: boolean;\n /** Enable debug logging of lifecycle events — defaults to false */\n debug?: boolean;\n /** Color theme — defaults to 'light' */\n theme?: SitepingTheme;\n /** UI locale — defaults to 'en'. Built-in: en, fr, de, es, it, pt (Brazilian), ru. Any other string falls back to English. */\n locale?: SitepingLocale | undefined;\n /**\n * Returns the current page scope for annotations and panel filtering.\n * Called on initial markers load and on `instance.refresh()`.\n *\n * Default: `{ url: window.location.pathname, urlPattern: null }` — annotations\n * are scoped strictly to the current pathname.\n *\n * Apps with parameterized routes (e.g. React Router) should return both the\n * concrete URL and the route template (e.g. `/orders/:orderId`) so the panel\n * can offer a \"this type of page\" filter that groups feedbacks by template.\n */\n getPageScope?: (() => PageScope) | undefined;\n /**\n * When true (default), the widget filters initial markers and panel results\n * by `feedback.url === scope.url`, so annotations created on one page never\n * leak to other pages — even if their CSS selector accidentally matches.\n * Set to `false` to revert to the legacy project-wide behavior.\n */\n scopeAnnotationsByUrl?: boolean | undefined;\n /**\n * Capture a JPEG screenshot of the annotated area on submit. Defaults to\n * `false` — opt-in because:\n *\n * - it adds runtime weight (~40 KB gzip dynamic chunk for html2canvas,\n * loaded only on first capture),\n * - it embeds page content in the feedback (privacy/GDPR consideration —\n * inform end users in your widget host UI when enabling).\n *\n * `html2canvas` ships as a regular dependency of `@siteping/widget` so the\n * dynamic import always resolves; you don't need to install anything extra.\n *\n * **Masking sensitive elements:** add `data-siteping-ignore=\"true\"` to any\n * element you do NOT want captured (password fields, credit-card forms,\n * API tokens shown in the UI, etc.). The capture predicate skips matching\n * elements *and their descendants*. Do this BEFORE turning on screenshots\n * in production — once a feedback is saved, the screenshot is in your DB\n * (or object storage) regardless of what was on the page.\n */\n enableScreenshot?: boolean | undefined;\n /**\n * Capture the last few `console.*` calls and failed network requests\n * (HTTP >= 400 or network error) at the moment a feedback is submitted.\n *\n * Lets reviewers replay the technical context that led to the report —\n * stack traces, 500 responses, dead third-party scripts. Great for the\n * \"the page just doesn't work\" feedback that contains zero detail.\n *\n * - `true` — capture with defaults (50 console / 20 network entries).\n * - `false` (default) — no capture, no monkey-patching.\n * - object — per-channel toggles + custom buffer sizes.\n *\n * **Privacy considerations:** console messages may contain anything the\n * host page logs, including user data. Failed network requests record the\n * URL (with query string) but never the response body. Inform end users\n * before enabling in environments where they might log sensitive values.\n */\n captureDiagnostics?: boolean | DiagnosticsCaptureOptions | undefined;\n /** Called when the widget is skipped (production mode, mobile viewport) */\n onSkip?: (reason: SitepingSkipReason) => void;\n /**\n * Auto-focus a specific annotation when its ID appears in the URL query\n * string. Lets hosts deeplink directly into a feedback from external\n * systems (Zammad tickets, Slack notifications, dashboard rows).\n *\n * When enabled, the widget reads the configured query parameter from\n * `window.location.search` right after the initial markers load. If the\n * value matches a visible feedback ID, the widget scrolls the annotation\n * into view, pins its highlight, and pulses the marker — the same visual\n * affordance a marker click produces.\n *\n * - `false` / `undefined` (default): no URL parsing. Existing behavior\n * unchanged, no host URL inspection.\n * - `true`: enabled with default query parameter name `siteping`.\n * - object: enabled with a custom parameter name. Use this to avoid\n * clashes with host-app query keys.\n *\n * Only the initial load triggers focus. Subsequent URL changes (SPA\n * navigation, `history.pushState`, hash updates) are ignored —\n * deliberate, to avoid surprising re-scrolls during normal browsing.\n * Hosts that need re-focus on route change can call\n * `instance.focusFeedback(id)` explicitly.\n */\n deepLink?: boolean | SitepingDeepLinkOptions | undefined;\n /**\n * Pre-fill author identity from the host application — typically the\n * currently signed-in user. When set, the widget uses these values\n * directly and never shows the identity modal, even on first feedback.\n *\n * Use case: SSO-integrated apps where the end user is already\n * authenticated by the host. Avoids the awkward \"enter your name and\n * email\" prompt for users the host already knows.\n *\n * When unset (default), the widget falls back to localStorage and shows\n * the modal on first feedback as before — existing behavior unchanged.\n *\n * Note: `config.identity` is **not** persisted to localStorage. It is\n * read at widget init time, not on every render. Hosts that need live\n * identity updates after sign-in/sign-out should currently remount the\n * widget (e.g. via a React `key` on the wrapping component). See\n * https://github.com/NeosiaNexus/SitePing/issues/85 for tracking a\n * future enhancement that propagates identity updates without a remount.\n */\n identity?: SitepingIdentity | undefined;\n\n // Events\n /** Called when the feedback panel is opened. */\n onOpen?: () => void;\n /** Called when the feedback panel is closed. */\n onClose?: () => void;\n onFeedbackSent?: (feedback: FeedbackResponse) => void;\n /**\n * Called when a feedback API call fails.\n *\n * The widget always emits a `SitepingError` (or a subclass:\n * `SitepingNetworkError`, `SitepingValidationError`, `SitepingAuthError`)\n * for HTTP-mode failures — host apps can `instanceof` to drive retry\n * logic, or read `error.code` (`\"NETWORK\" | \"VALIDATION\" | \"AUTH\" |\n * \"SERVER\"`) and `error.retryable`. The type is widened to `Error` so\n * direct-store callers can still surface raw errors without breaking the\n * contract.\n */\n onError?: (error: Error) => void;\n /** Called when the user starts drawing an annotation. */\n onAnnotationStart?: () => void;\n /** Called when the user finishes drawing an annotation. */\n onAnnotationEnd?: () => void;\n}\n\n/** Instance returned by initSiteping() with lifecycle methods. */\nexport interface SitepingInstance {\n /** Remove the widget from the DOM and clean up all listeners. */\n destroy: () => void;\n /** Open the panel programmatically */\n open: () => void;\n /** Close the panel */\n close: () => void;\n /** Reload feedbacks from server */\n refresh: () => void;\n /**\n * Scroll the matching annotation into view, pin its highlight, and\n * pulse its marker. Returns `true` when a visible feedback matched the\n * given ID, `false` otherwise (unknown ID, feedback on another URL when\n * `scopeAnnotationsByUrl` filtered it out, or markers not yet loaded).\n *\n * Counterpart to the `deepLink` config option for hosts that prefer to\n * drive focus from JS (e.g., a notification click handler) instead of a\n * URL query parameter.\n */\n focusFeedback: (feedbackId: string) => boolean;\n /** Subscribe to a public widget event */\n on: <K extends keyof SitepingPublicEvents>(event: K, listener: SitepingPublicEventListener<K>) => SitepingUnsubscribe;\n /** Unsubscribe from a public widget event */\n off: <K extends keyof SitepingPublicEvents>(event: K, listener: SitepingPublicEventListener<K>) => void;\n}\n\n/** Listener signature for a single `SitepingPublicEvents` key. */\nexport type SitepingPublicEventListener<K extends keyof SitepingPublicEvents> = (\n ...args: SitepingPublicEvents[K]\n) => void;\n\n/** Disposer returned by `SitepingInstance.on` — call once to detach the listener. */\nexport type SitepingUnsubscribe = () => void;\n\n/** Events exposed to consumers via SitepingInstance.on / .off */\nexport interface SitepingPublicEvents {\n \"feedback:sent\": [FeedbackResponse];\n \"feedback:deleted\": [FeedbackResponse[\"id\"]];\n \"panel:open\": [];\n \"panel:close\": [];\n}\n\n// ---------------------------------------------------------------------------\n// Feedback\n// ---------------------------------------------------------------------------\n\n/** Single source of truth for feedback types — used by both TS types and Zod schemas. */\nexport const FEEDBACK_TYPES = [\"question\", \"change\", \"bug\", \"other\"] as const;\nexport type FeedbackType = (typeof FEEDBACK_TYPES)[number];\n\n/** Single source of truth for feedback statuses. */\nexport const FEEDBACK_STATUSES = [\"open\", \"resolved\"] as const;\nexport type FeedbackStatus = (typeof FEEDBACK_STATUSES)[number];\n\n/**\n * Page scope returned by `SitepingConfig.getPageScope()`.\n *\n * - `url`: concrete page identifier — usually `window.location.pathname`,\n * used as the strict scope for marker rendering.\n * - `urlPattern`: optional parameterized template (e.g. `/orders/:orderId`)\n * used by the panel's \"this type of page\" filter to group feedbacks across\n * instances of the same page kind.\n */\nexport interface PageScope {\n url: string;\n urlPattern: string | null;\n}\n\n// ---------------------------------------------------------------------------\n// Abstract Store — adapter pattern\n// ---------------------------------------------------------------------------\n\n/** Input for creating a feedback record in the store. */\nexport interface FeedbackCreateInput {\n projectName: string;\n type: FeedbackType;\n message: string;\n status: FeedbackStatus;\n url: string;\n /**\n * Optional parameterized URL template (e.g. `/orders/:orderId`) for the page\n * where the feedback was created. Allows the panel to filter feedbacks by\n * \"this type of page\" across different instances. Null when the host did not\n * provide a `getPageScope` callback or the route has no template.\n */\n urlPattern?: string | null | undefined;\n viewport: string;\n userAgent: string;\n authorName: string;\n authorEmail: string;\n clientId: string;\n annotations: AnnotationCreateInput[];\n /**\n * Base64 JPEG `data:` URL captured by the widget at submit time.\n *\n * Adapters with a configured `ScreenshotStorage` are expected to upload\n * this and persist the returned URL on `FeedbackRecord.screenshotUrl`.\n * Adapters without storage may persist the data URL inline (memory /\n * localStorage / dev) — the widget then renders it directly.\n */\n screenshotDataUrl?: string | null | undefined;\n /**\n * Optional console + failed-network snapshot captured by the widget when\n * `SitepingConfig.captureDiagnostics` is enabled. Stored as JSON on\n * `FeedbackRecord.diagnostics` so reviewers can replay the context.\n */\n diagnostics?: DiagnosticsSnapshot | null | undefined;\n}\n\n/** Input for a single annotation when creating a feedback. */\nexport interface AnnotationCreateInput {\n cssSelector: string;\n xpath: string;\n textSnippet: string;\n elementTag: string;\n elementId?: string | undefined;\n textPrefix: string;\n textSuffix: string;\n fingerprint: string;\n neighborText: string;\n /**\n * Semantic anchor identifier from the closest ancestor's `data-feedback-anchor`\n * attribute. When set, this is the most stable re-anchoring signal because\n * hosts deliberately place these on layout/section roots that survive DOM\n * refactors and viewport changes. Null when no semantic ancestor exists.\n */\n anchorKey?: string | null | undefined;\n xPct: number;\n yPct: number;\n wPct: number;\n hPct: number;\n scrollX: number;\n scrollY: number;\n viewportW: number;\n viewportH: number;\n devicePixelRatio: number;\n}\n\n/** Query parameters for fetching feedbacks. */\nexport interface FeedbackQuery {\n projectName: string;\n type?: FeedbackType | undefined;\n status?: FeedbackStatus | undefined;\n search?: string | undefined;\n page?: number | undefined;\n limit?: number | undefined;\n /**\n * Filter to feedbacks created on this exact URL (path). Used by the panel's\n * \"this page\" filter and by the markers loader to keep page scopes isolated.\n */\n url?: string | undefined;\n /**\n * Filter to feedbacks created on this URL pattern (e.g. `/orders/:orderId`).\n * Used by the panel's \"this type of page\" filter to group feedbacks across\n * different concrete instances of the same template.\n */\n urlPattern?: string | undefined;\n}\n\n/** Update payload for patching a feedback. */\nexport interface FeedbackUpdateInput {\n status: FeedbackStatus;\n resolvedAt: Date | null;\n}\n\n/** A persisted feedback record returned by the store. */\nexport interface FeedbackRecord {\n id: string;\n type: FeedbackType;\n message: string;\n status: FeedbackStatus;\n projectName: string;\n url: string;\n /**\n * Parameterized URL template the feedback was created on.\n * Null for legacy records or hosts without `getPageScope`.\n */\n urlPattern: string | null;\n authorName: string;\n authorEmail: string;\n viewport: string;\n userAgent: string;\n clientId: string;\n resolvedAt: Date | null;\n createdAt: Date;\n updatedAt: Date;\n annotations: AnnotationRecord[];\n /**\n * URL the widget renders as `<img src>`. Either an `https://...` from a\n * configured `ScreenshotStorage`, or a `data:image/jpeg;base64,...` URL\n * inline-persisted by adapters without storage. Null when no screenshot\n * was captured (legacy records, capture failed, or host disabled it).\n */\n screenshotUrl: string | null;\n /**\n * Console + failed-network snapshot captured at submit time. Null when\n * diagnostics weren't enabled on the widget side.\n */\n diagnostics: DiagnosticsSnapshot | null;\n}\n\n/** A persisted annotation record returned by the store. */\nexport interface AnnotationRecord {\n id: string;\n feedbackId: string;\n cssSelector: string;\n xpath: string;\n textSnippet: string;\n elementTag: string;\n elementId: string | null;\n textPrefix: string;\n textSuffix: string;\n fingerprint: string;\n neighborText: string;\n /**\n * Semantic anchor identifier from `data-feedback-anchor`. Null for legacy\n * annotations or those drawn outside any anchored region.\n */\n anchorKey: string | null;\n xPct: number;\n yPct: number;\n wPct: number;\n hPct: number;\n scrollX: number;\n scrollY: number;\n viewportW: number;\n viewportH: number;\n devicePixelRatio: number;\n createdAt: Date;\n}\n\n// ---------------------------------------------------------------------------\n// Store errors — throw these from adapter implementations\n// ---------------------------------------------------------------------------\n\n/**\n * Thrown when a record is not found during update or delete.\n *\n * Handlers translate this to HTTP 404. Adapters MUST throw this (not\n * ORM-specific errors) so the handler layer remains ORM-agnostic.\n */\nexport class StoreNotFoundError extends Error {\n readonly code = \"STORE_NOT_FOUND\" as const;\n constructor(message = \"Record not found\") {\n super(message);\n this.name = \"StoreNotFoundError\";\n }\n}\n\n/**\n * Thrown when a unique constraint is violated (e.g. duplicate `clientId`).\n *\n * Handlers use this to return the existing record instead of failing.\n */\nexport class StoreDuplicateError extends Error {\n readonly code = \"STORE_DUPLICATE\" as const;\n constructor(message = \"Duplicate record\") {\n super(message);\n this.name = \"StoreDuplicateError\";\n }\n}\n\n/** Shape of any ORM error that carries a Prisma-style `code` field. */\ntype CodedError<C extends string = string> = { code: C };\n\nfunction hasErrorCode<C extends string>(error: unknown, code: C): error is CodedError<C> {\n return hasOwn(error, \"code\") && (error as { code: unknown }).code === code;\n}\n\n/** Type guard — works for `StoreNotFoundError` and ORM-specific equivalents (e.g. Prisma P2025). */\nexport function isStoreNotFound(error: unknown): error is StoreNotFoundError | CodedError<\"P2025\"> {\n if (error instanceof StoreNotFoundError) return true;\n // Backwards compat: Prisma's P2025\n return hasErrorCode(error, \"P2025\");\n}\n\n/** Type guard — works for `StoreDuplicateError` and ORM-specific equivalents (e.g. Prisma P2002). */\nexport function isStoreDuplicate(error: unknown): error is StoreDuplicateError | CodedError<\"P2002\"> {\n if (error instanceof StoreDuplicateError) return true;\n // Backwards compat: Prisma's P2002\n return hasErrorCode(error, \"P2002\");\n}\n\n// ---------------------------------------------------------------------------\n// Store helpers — shared conversion logic for adapters\n// ---------------------------------------------------------------------------\n\n/** Flatten a widget `AnnotationPayload` (nested anchor + rect) into a flat `AnnotationCreateInput`. */\nexport function flattenAnnotation(ann: AnnotationPayload): Prettify<AnnotationCreateInput> {\n return {\n cssSelector: ann.anchor.cssSelector,\n xpath: ann.anchor.xpath,\n textSnippet: ann.anchor.textSnippet,\n elementTag: ann.anchor.elementTag,\n elementId: ann.anchor.elementId,\n textPrefix: ann.anchor.textPrefix,\n textSuffix: ann.anchor.textSuffix,\n fingerprint: ann.anchor.fingerprint,\n neighborText: ann.anchor.neighborText,\n anchorKey: ann.anchor.anchorKey ?? null,\n xPct: ann.rect.xPct,\n yPct: ann.rect.yPct,\n wPct: ann.rect.wPct,\n hPct: ann.rect.hPct,\n scrollX: ann.scrollX,\n scrollY: ann.scrollY,\n viewportW: ann.viewportW,\n viewportH: ann.viewportH,\n devicePixelRatio: ann.devicePixelRatio,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Abstract Store — adapter pattern\n// ---------------------------------------------------------------------------\n\n/** Paginated result returned by `SitepingStore.getFeedbacks`. */\nexport interface FeedbackPage {\n feedbacks: FeedbackRecord[];\n total: number;\n}\n\n/**\n * Abstract storage interface for Siteping.\n *\n * Any adapter (Prisma, Drizzle, raw SQL, localStorage, etc.) implements this\n * interface. The HTTP handler and widget `StoreClient` operate against\n * `SitepingStore`, decoupled from the storage backend.\n *\n * ## Error contract\n *\n * - **`updateFeedback` / `deleteFeedback`**: throw `StoreNotFoundError` when\n * the record does not exist.\n * - **`createFeedback`**: either return the existing record on duplicate\n * `clientId` (idempotent) or throw `StoreDuplicateError`. The handler\n * handles both patterns.\n * - Other methods should not throw on empty results — return empty arrays or `null`.\n */\nexport interface SitepingStore {\n /** Create a feedback with its annotations. Idempotent on `clientId` — return existing record on duplicate, or throw `StoreDuplicateError`. */\n createFeedback(data: FeedbackCreateInput): Promise<FeedbackRecord>;\n /** Paginated query with optional filters. Returns empty array (not error) when no results. */\n getFeedbacks(query: FeedbackQuery): Promise<FeedbackPage>;\n /** Lookup by client-generated UUID. Returns `null` (not error) when not found. */\n findByClientId(clientId: string): Promise<FeedbackRecord | null>;\n /** Update status/resolvedAt. Throws `StoreNotFoundError` if `id` does not exist. */\n updateFeedback(id: string, data: FeedbackUpdateInput): Promise<FeedbackRecord>;\n /** Delete a single record. Throws `StoreNotFoundError` if `id` does not exist. */\n deleteFeedback(id: string): Promise<void>;\n /** Bulk delete all feedbacks for a project. No-op (not error) if none exist. */\n deleteAllFeedbacks(projectName: string): Promise<void>;\n}\n\n/** Payload sent from the widget to the server when submitting feedback. */\nexport interface FeedbackPayload {\n projectName: string;\n type: FeedbackType;\n message: string;\n url: string;\n /**\n * Parameterized URL template (e.g. `/orders/:orderId`) supplied by\n * `SitepingConfig.getPageScope()`. Null when the host did not provide one.\n */\n urlPattern?: string | null | undefined;\n viewport: string;\n userAgent: string;\n authorName: string;\n authorEmail: string;\n annotations: AnnotationPayload[];\n /** Client-generated UUID for deduplication */\n clientId: string;\n /**\n * Base64 JPEG `data:` URL of the annotated area. Captured by the widget\n * when `enableScreenshot: true` is set in `SitepingConfig`. Null when\n * disabled or when capture failed silently.\n */\n screenshotDataUrl?: string | null | undefined;\n /**\n * Snapshot of the last few console messages and failed network requests\n * captured at submit time when `captureDiagnostics` is enabled.\n */\n diagnostics?: DiagnosticsSnapshot | null | undefined;\n}\n\n/** Severity levels persisted in `ConsoleDiagnosticEntry`. */\nexport type ConsoleDiagnosticLevel = \"log\" | \"info\" | \"warn\" | \"error\";\n\n/** A single console entry captured by `ConsoleBuffer`. */\nexport interface ConsoleDiagnosticEntry {\n level: ConsoleDiagnosticLevel;\n /** ISO 8601 timestamp captured at log time. */\n timestamp: string;\n /** Best-effort string representation of the original console args. */\n message: string;\n}\n\n/** A single failed network request captured by `NetworkBuffer`. */\nexport interface NetworkDiagnosticEntry {\n url: string;\n method: string;\n /** HTTP status; 0 when the request never reached the server. */\n status: number;\n /** End-to-end duration in ms. */\n durationMs: number;\n /** ISO 8601 timestamp at the moment the request was initiated. */\n timestamp: string;\n}\n\n/**\n * Diagnostics captured by the widget when `captureDiagnostics` is enabled.\n *\n * Both arrays are bounded (default: 50 console / 20 network). Adapters that\n * support diagnostics should persist this as a JSON blob alongside the\n * feedback so reviewers can replay the context that led to the report.\n */\nexport interface DiagnosticsSnapshot {\n console: ConsoleDiagnosticEntry[];\n network: NetworkDiagnosticEntry[];\n}\n\n// ---------------------------------------------------------------------------\n// Annotation — multi-selector anchoring (Hypothesis / W3C Web Annotation)\n// ---------------------------------------------------------------------------\n\n/** DOM anchoring data for re-attaching annotations to page elements. */\nexport interface AnchorData {\n /** CSS selector generated by @medv/finder — primary anchor */\n cssSelector: string;\n /** XPath — fallback 1 */\n xpath: string;\n /** First ~120 chars of element innerText — empty string if none */\n textSnippet: string;\n /** Tag name for validation (e.g. \"DIV\", \"SECTION\") */\n elementTag: string;\n /** Element id attribute if available — most stable */\n elementId?: string | undefined;\n /** ~32 chars of text before this element in document flow (disambiguation) */\n textPrefix: string;\n /** ~32 chars of text after this element in document flow (disambiguation) */\n textSuffix: string;\n /** Structural fingerprint: \"childCount:siblingIdx:attrHash\" */\n fingerprint: string;\n /** Text content of adjacent sibling elements (context) */\n neighborText: string;\n /**\n * Semantic anchor identifier from the closest ancestor's `data-feedback-anchor`\n * attribute. When set, this is the highest-priority re-anchoring signal —\n * hosts deliberately place these on layout/section roots that survive\n * viewport changes and DOM refactors.\n */\n anchorKey?: string | null | undefined;\n}\n\n/** Drawn rectangle coordinates as percentages relative to the anchor element. */\nexport interface RectData {\n /** X offset as fraction of anchor element width — must be in range [0, 1] */\n xPct: number;\n /** Y offset as fraction of anchor element height — must be in range [0, 1] */\n yPct: number;\n /** Width as fraction of anchor element width — must be in range [0, 1] */\n wPct: number;\n /** Height as fraction of anchor element height — must be in range [0, 1] */\n hPct: number;\n}\n\n/** Annotation data sent as part of a feedback submission. */\nexport interface AnnotationPayload {\n anchor: AnchorData;\n rect: RectData;\n scrollX: number;\n scrollY: number;\n viewportW: number;\n viewportH: number;\n devicePixelRatio: number;\n}\n\n// ---------------------------------------------------------------------------\n// API responses\n// ---------------------------------------------------------------------------\n\n/** Feedback record as returned by the API (dates serialized as strings). */\nexport interface FeedbackResponse {\n id: string;\n projectName: string;\n type: FeedbackType;\n message: string;\n status: FeedbackStatus;\n url: string;\n /** Parameterized URL template the feedback was created on, or null. */\n urlPattern: string | null;\n viewport: string;\n userAgent: string;\n authorName: string;\n authorEmail: string;\n resolvedAt: string | null;\n createdAt: string;\n updatedAt: string;\n annotations: AnnotationResponse[];\n /** Screenshot URL (data: or http:) — see `FeedbackRecord.screenshotUrl`. */\n screenshotUrl: string | null;\n /** Console + failed-network snapshot, or null when diagnostics weren't captured. */\n diagnostics: DiagnosticsSnapshot | null;\n}\n\n/** Annotation record as returned by the API. */\nexport interface AnnotationResponse {\n id: string;\n feedbackId: string;\n cssSelector: string;\n xpath: string;\n textSnippet: string;\n elementTag: string;\n elementId: string | null;\n textPrefix: string;\n textSuffix: string;\n fingerprint: string;\n neighborText: string;\n /** Semantic anchor identifier from `data-feedback-anchor`, or null. */\n anchorKey: string | null;\n xPct: number;\n yPct: number;\n wPct: number;\n hPct: number;\n scrollX: number;\n scrollY: number;\n viewportW: number;\n viewportH: number;\n devicePixelRatio: number;\n createdAt: string;\n}\n\n/** Paginated `FeedbackResponse` shape returned by the API. */\nexport interface FeedbackResponseList {\n feedbacks: FeedbackResponse[];\n total: number;\n}\n","import type { Translations } from \"./types.js\";\n\nexport const en: Translations = {\n // Panel\n \"panel.title\": \"Feedbacks\",\n \"panel.ariaLabel\": \"Siteping feedback panel\",\n \"panel.feedbackList\": \"Feedback list\",\n \"panel.loading\": \"Loading feedbacks\",\n \"panel.close\": \"Close panel\",\n \"panel.deleteAll\": \"Delete all\",\n \"panel.deleteAllConfirmTitle\": \"Delete all\",\n \"panel.deleteAllConfirmMessage\": \"Delete all feedbacks for this project? This action cannot be undone.\",\n \"panel.search\": \"Search...\",\n \"panel.searchAria\": \"Search feedbacks\",\n \"panel.filterAll\": \"All\",\n \"panel.loadError\": \"Failed to load\",\n \"panel.retry\": \"Retry\",\n \"panel.empty\": \"No feedback yet\",\n \"panel.showMore\": \"Show more\",\n \"panel.showLess\": \"Show less\",\n \"panel.resolve\": \"Resolve\",\n \"panel.reopen\": \"Reopen\",\n \"panel.delete\": \"Delete\",\n \"panel.cancel\": \"Cancel\",\n \"panel.confirmDelete\": \"Delete\",\n \"panel.loadMore\": \"Load more ({remaining} remaining)\",\n\n // Status filter labels\n \"panel.statusAll\": \"All\",\n \"panel.statusOpen\": \"Open\",\n \"panel.statusResolved\": \"Resolved\",\n\n // Feedback type labels\n \"type.label\": \"Type\",\n \"type.question\": \"Question\",\n \"type.change\": \"Change\",\n \"type.bug\": \"Bug\",\n \"type.other\": \"Other\",\n\n // Status segmented control label\n \"status.label\": \"Status\",\n\n // Page scope segmented control\n \"scope.label\": \"Scope\",\n \"scope.thisPage\": \"This page\",\n \"scope.thisType\": \"This type\",\n \"scope.all\": \"All pages\",\n\n // FAB menu\n \"fab.aria\": \"Siteping \\u2014 Feedback menu\",\n \"fab.messages\": \"Messages\",\n \"fab.annotate\": \"Annotate\",\n \"fab.annotations\": \"Annotations\",\n\n // Annotator\n \"annotator.instruction\": \"Draw a rectangle on the area to comment\",\n \"annotator.cancel\": \"Cancel\",\n\n // Popup\n \"popup.ariaLabel\": \"Feedback form\",\n \"popup.placeholder\": \"Describe your feedback...\",\n \"popup.textareaAria\": \"Feedback message\",\n \"popup.submitHintMac\": \"\\u2318+Enter to send\",\n \"popup.submitHintOther\": \"Ctrl+Enter to send\",\n \"popup.cancel\": \"Cancel\",\n \"popup.submit\": \"Send\",\n\n // Identity modal\n \"identity.title\": \"Identify yourself\",\n \"identity.nameLabel\": \"Name\",\n \"identity.namePlaceholder\": \"Your name\",\n \"identity.emailLabel\": \"Email\",\n \"identity.emailPlaceholder\": \"your@email.com\",\n \"identity.cancel\": \"Cancel\",\n \"identity.submit\": \"Continue\",\n\n // Markers\n \"marker.approximate\": \"Approximate position (confidence: {confidence}%)\",\n \"marker.aria\": \"Feedback #{number}: {type} — {message}\",\n \"marker.count\": \"{count} feedback markers displayed\",\n\n // FAB badge\n \"fab.badge\": \"{count} unresolved feedbacks\",\n\n // Accessibility — screen reader announcements\n \"feedback.sent.confirmation\": \"Feedback sent successfully\",\n \"feedback.error.message\": \"Failed to send feedback\",\n \"feedback.deleted.confirmation\": \"Feedback deleted\",\n\n // Badge\n \"badge.count\": \"{count} unresolved feedbacks\",\n\n // Bulk actions toolbar\n \"bulk.selectAll\": \"Select all\",\n \"bulk.selected\": \"{count} selected\",\n \"bulk.resolve\": \"Resolve\",\n \"bulk.delete\": \"Delete\",\n \"bulk.deselect\": \"Deselect\",\n\n // Sort and group controls\n \"sort.newest\": \"Newest first\",\n \"sort.oldest\": \"Oldest first\",\n \"sort.byType\": \"By type\",\n \"sort.openFirst\": \"Open first\",\n \"sort.label\": \"Sort\",\n \"group.byPage\": \"By page\",\n \"group.feedbacks\": \"{count} feedbacks\",\n\n // Stats bar\n \"stats.open\": \"Open\",\n \"stats.resolved\": \"Resolved\",\n \"stats.bugs\": \"Bugs\",\n \"stats.progress\": \"{percent}% resolved\",\n\n // Detail view\n \"detail.back\": \"Back\",\n \"detail.title\": \"Feedback #{number}\",\n \"detail.status\": \"Status\",\n \"detail.message\": \"Message\",\n \"detail.screenshot\": \"Screenshot\",\n \"detail.screenshotAlt\": \"Screenshot of the annotated area\",\n \"detail.metadata\": \"Details\",\n \"detail.annotation\": \"Annotation\",\n \"detail.page\": \"Page\",\n \"detail.author\": \"Author\",\n \"detail.date\": \"Created\",\n \"detail.viewport\": \"Viewport\",\n \"detail.browser\": \"Browser\",\n \"detail.resolvedAt\": \"Resolved at\",\n \"detail.goToAnnotation\": \"Go to annotation\",\n \"detail.element\": \"Element\",\n \"detail.selector\": \"Selector\",\n \"detail.position\": \"Position\",\n \"detail.resolve\": \"Resolve\",\n \"detail.reopen\": \"Reopen\",\n \"detail.delete\": \"Delete\",\n \"detail.diagnostics\": \"Diagnostics\",\n \"detail.diagnostics.console\": \"Console\",\n \"detail.diagnostics.network\": \"Failed network\",\n \"detail.diagnostics.expand\": \"Show diagnostics\",\n \"detail.diagnostics.collapse\": \"Hide diagnostics\",\n \"detail.diagnostics.noEntries\": \"No entries\",\n\n // Keyboard shortcuts overlay\n \"shortcuts.title\": \"Keyboard shortcuts\",\n \"shortcuts.navigate\": \"Navigate feedbacks\",\n \"shortcuts.resolve\": \"Resolve / Reopen\",\n \"shortcuts.delete\": \"Delete\",\n \"shortcuts.search\": \"Focus search\",\n \"shortcuts.select\": \"Toggle selection\",\n \"shortcuts.help\": \"Show shortcuts\",\n \"shortcuts.close\": \"Close\",\n \"shortcuts.hint\": \"Keyboard shortcuts\",\n\n // Export controls\n \"export.label\": \"Export\",\n \"export.csv\": \"Export CSV\",\n \"export.json\": \"Export JSON\",\n};\n","import type { BuiltinLocale, FeedbackType } from \"@siteping/core\";\nimport { BUILTIN_LOCALES } from \"@siteping/core\";\nimport type { TFunction, TranslationKey, Translations } from \"./types.js\";\n\nexport type { TFunction, TranslationKey, Translations } from \"./types.js\";\n\n// `en` is bundled synchronously as the immediate fallback — every other\n// locale is dynamically imported on demand to keep the initial bundle small.\n// In practice the bundler emits one chunk per locale and only the resolved\n// one ships over the network when `loadLocale()` is called.\nimport { en } from \"./en.js\";\n\nconst LOCALES: Record<string, Translations> = { en };\n\n/** Built-in locales other than the synchronously-bundled English fallback. */\nconst BUILTIN_NON_EN: ReadonlySet<BuiltinLocale> = new Set(BUILTIN_LOCALES.filter((l) => l !== \"en\"));\n\nfunction isBuiltinNonEn(lang: string): lang is Exclude<BuiltinLocale, \"en\"> {\n return (BUILTIN_NON_EN as ReadonlySet<string>).has(lang);\n}\n\n/** Normalise a BCP-47 tag down to the base language used for dictionary lookups. */\nfunction normaliseLang(locale: string): string {\n return (locale.split(\"-\")[0] ?? locale).toLowerCase();\n}\n\n/** Register a custom locale at runtime. */\nexport function registerLocale(code: string, translations: Translations): void {\n LOCALES[code] = translations;\n}\n\n/**\n * Dynamically import a built-in locale and register it. Returns the loaded\n * translations or `null` if the locale isn't a known built-in. Custom locales\n * registered via {@link registerLocale} bypass this loader — they are already\n * in the registry.\n */\nexport async function loadLocale(locale: string): Promise<Translations | null> {\n const lang = normaliseLang(locale);\n const cached = LOCALES[lang];\n if (cached) return cached; // already loaded (en, custom, or previously fetched)\n if (!isBuiltinNonEn(lang)) return null;\n // The static switch means tsup/esbuild will create one chunk per locale\n // and only the requested one is fetched at runtime.\n let mod: Partial<Record<BuiltinLocale, Translations>>;\n switch (lang) {\n case \"de\":\n mod = await import(\"./de.js\");\n break;\n case \"es\":\n mod = await import(\"./es.js\");\n break;\n case \"fr\":\n mod = await import(\"./fr.js\");\n break;\n case \"it\":\n mod = await import(\"./it.js\");\n break;\n case \"pt\":\n mod = await import(\"./pt.js\");\n break;\n case \"ru\":\n mod = await import(\"./ru.js\");\n break;\n default:\n return null;\n }\n const dict = mod[lang];\n if (!dict) return null;\n LOCALES[lang] = dict;\n return dict;\n}\n\n/**\n * Create a translation function for the given locale.\n *\n * Locale resolution: exact match > language prefix > English fallback.\n * Non-English built-in locales are lazy-loaded via {@link loadLocale} — call\n * `await loadLocale(locale)` at init if you want the panel to render in the\n * target language immediately. Otherwise the widget renders in English until\n * the dictionary lands, then `createT` returns the resolved dictionary.\n */\nexport function createT(locale: string): TFunction {\n const lang = normaliseLang(locale);\n if (lang !== \"en\" && !LOCALES[lang] && !isBuiltinNonEn(lang)) {\n console.warn(`[siteping] Unknown locale \"${locale}\", falling back to \"en\"`);\n }\n // Read LOCALES at call time so `createT` returns up-to-date translations\n // after `loadLocale` has registered the dictionary asynchronously.\n return (key) => {\n const dict = LOCALES[lang] ?? LOCALES.en;\n return dict?.[key] ?? LOCALES.en?.[key] ?? key;\n };\n}\n\n/**\n * Returns the type label for a `FeedbackType` value.\n *\n * Maps API enum values (English) to localised display labels. The exhaustive\n * `switch` is paired with a `never` check so adding a new `FeedbackType`\n * surfaces here at compile time.\n */\nexport function getTypeLabel(type: FeedbackType | string, t: TFunction): string {\n switch (type) {\n case \"question\":\n return t(\"type.question\");\n case \"change\":\n return t(\"type.change\");\n case \"bug\":\n return t(\"type.bug\");\n case \"other\":\n return t(\"type.other\");\n default:\n return type;\n }\n}\n\n/**\n * Interpolate `{paramName}` placeholders in a translated string with the\n * values from `params`. Stringifies numbers and booleans inline so callers\n * can pass `t(\"marker.count\")` along with `{ count: 3 }` directly.\n *\n * Unknown placeholders are left as-is — matches the existing behaviour of\n * inline `.replace(\"{count}\", ...)` calls scattered across the widget.\n */\nexport function interpolate(template: string, params: Readonly<Record<string, string | number | boolean>>): string {\n return template.replace(/\\{(\\w+)\\}/g, (match, name: string) => {\n const value = params[name];\n return value === undefined ? match : String(value);\n });\n}\n\n/** Shorthand for `interpolate(t(key), params)`. Typed against `TranslationKey`. */\nexport function tWithParams(\n t: TFunction,\n key: TranslationKey,\n params: Readonly<Record<string, string | number | boolean>>,\n): string {\n return interpolate(t(key), params);\n}\n","import type { FeedbackResponse } from \"@siteping/core\";\nimport { el, parseSvg, setText } from \"./dom-utils.js\";\nimport type { TFunction } from \"./i18n/index.js\";\nimport type { ThemeColors } from \"./styles/theme.js\";\n\n// ---------------------------------------------------------------------------\n// Icons\n// ---------------------------------------------------------------------------\n\nexport const ICON_EXPORT = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"/><polyline points=\"7 10 12 15 17 10\"/><line x1=\"12\" y1=\"15\" x2=\"12\" y2=\"3\"/></svg>`;\n\nconst ICON_CSV = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/><line x1=\"3\" y1=\"9\" x2=\"21\" y2=\"9\"/><line x1=\"3\" y1=\"15\" x2=\"21\" y2=\"15\"/><line x1=\"9\" y1=\"3\" x2=\"9\" y2=\"21\"/><line x1=\"15\" y1=\"3\" x2=\"15\" y2=\"21\"/></svg>`;\n\nconst ICON_JSON = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M8 3H6a2 2 0 0 0-2 2v4a2 2 0 0 1-2 2 2 2 0 0 1 2 2v4a2 2 0 0 0 2 2h2\"/><path d=\"M16 3h2a2 2 0 0 1 2 2v4a2 2 0 0 0 2 2 2 2 0 0 0-2 2v4a2 2 0 0 1-2 2h-2\"/></svg>`;\n\n// ---------------------------------------------------------------------------\n// CSS\n// ---------------------------------------------------------------------------\n\nexport const EXPORT_CSS = `\n /* ============================\n Export Button & Menu\n ============================ */\n\n .sp-export-btn {\n padding: 5px 12px;\n border-radius: var(--sp-radius-full);\n border: 1px solid var(--sp-border);\n background: transparent;\n color: var(--sp-text-tertiary);\n font-family: var(--sp-font);\n font-size: 11px;\n font-weight: 500;\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 4px;\n transition: all 0.2s ease;\n position: relative;\n }\n\n .sp-export-btn svg {\n width: 13px;\n height: 13px;\n }\n\n .sp-export-btn:hover {\n border-color: var(--sp-accent);\n color: var(--sp-accent);\n background: var(--sp-accent-light);\n }\n\n .sp-export-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n pointer-events: none;\n }\n\n .sp-export-menu {\n position: absolute;\n top: calc(100% + 6px);\n right: 0;\n min-width: 180px;\n padding: 4px;\n border-radius: var(--sp-radius);\n background: var(--sp-glass-bg-heavy);\n backdrop-filter: blur(var(--sp-blur));\n -webkit-backdrop-filter: blur(var(--sp-blur));\n border: 1px solid var(--sp-glass-border);\n box-shadow: var(--sp-shadow-lg);\n z-index: 10;\n opacity: 0;\n transform: translateY(-4px) scale(0.97);\n transition: opacity 0.15s ease, transform 0.15s ease;\n pointer-events: none;\n }\n\n .sp-export-menu--open {\n opacity: 1;\n transform: translateY(0) scale(1);\n pointer-events: auto;\n }\n\n .sp-export-option {\n display: flex;\n align-items: center;\n gap: 10px;\n width: 100%;\n padding: 8px 16px;\n border: none;\n border-radius: 8px;\n background: transparent;\n color: var(--sp-text-secondary);\n font-family: var(--sp-font);\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s ease;\n text-align: left;\n }\n\n .sp-export-option:hover,\n .sp-export-option:focus-visible {\n background: var(--sp-accent-light);\n color: var(--sp-accent);\n }\n\n .sp-export-option-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n }\n\n .sp-export-option-icon svg {\n width: 16px;\n height: 16px;\n }\n\n .sp-export-option-label {\n flex: 1;\n }\n\n @media (forced-colors: active) {\n .sp-export-btn,\n .sp-export-option,\n .sp-export-menu {\n border: 2px solid ButtonText !important;\n background: Canvas !important;\n color: ButtonText !important;\n }\n\n .sp-export-btn:focus-visible,\n .sp-export-option:focus-visible {\n outline: 3px solid Highlight !important;\n }\n }\n`;\n\n// ---------------------------------------------------------------------------\n// CSV / JSON conversion\n// ---------------------------------------------------------------------------\n\nconst CSV_COLUMNS = [\n \"id\",\n \"type\",\n \"status\",\n \"message\",\n \"url\",\n \"authorName\",\n \"authorEmail\",\n \"createdAt\",\n \"resolvedAt\",\n \"viewport\",\n] as const;\n\n/** Escape a value for CSV: wrap in double-quotes if it contains commas, newlines, or quotes. */\nfunction escapeCsvField(value: string): string {\n if (value.includes('\"') || value.includes(\",\") || value.includes(\"\\n\") || value.includes(\"\\r\")) {\n return `\"${value.replace(/\"/g, '\"\"')}\"`;\n }\n return value;\n}\n\n/** Convert feedbacks to CSV string */\nexport function feedbacksToCsv(feedbacks: FeedbackResponse[]): string {\n const header = CSV_COLUMNS.join(\",\");\n const rows = feedbacks.map((fb) =>\n CSV_COLUMNS.map((col) => {\n const raw = fb[col];\n return escapeCsvField(raw == null ? \"\" : String(raw));\n }).join(\",\"),\n );\n return [header, ...rows].join(\"\\n\");\n}\n\n/** Convert feedbacks to formatted JSON string */\nexport function feedbacksToJson(feedbacks: FeedbackResponse[]): string {\n return JSON.stringify(feedbacks, null, 2);\n}\n\n// ---------------------------------------------------------------------------\n// Download helper\n// ---------------------------------------------------------------------------\n\n/** Trigger browser download of a string as file */\nexport function downloadFile(content: string, filename: string, mimeType: string): void {\n const blob = new Blob([content], { type: mimeType });\n const url = URL.createObjectURL(blob);\n const anchor = document.createElement(\"a\");\n anchor.href = url;\n anchor.download = filename;\n anchor.style.display = \"none\";\n document.body.appendChild(anchor);\n anchor.click();\n // Clean up after a tick to ensure the download starts\n requestAnimationFrame(() => {\n URL.revokeObjectURL(url);\n anchor.remove();\n });\n}\n\n// ---------------------------------------------------------------------------\n// ExportButton component\n// ---------------------------------------------------------------------------\n\nexport class ExportButton {\n readonly element: HTMLElement;\n\n private menu: HTMLElement;\n private isOpen = false;\n private onDocumentClick: (e: MouseEvent) => void;\n\n constructor(\n _colors: ThemeColors,\n private readonly getFeedbacks: () => FeedbackResponse[],\n t: TFunction,\n ) {\n // Wrapper for relative positioning of the menu\n this.element = el(\"div\", { style: \"position: relative; display: inline-flex;\" });\n\n // Trigger button — matches .sp-btn-delete-all pill style\n const btn = document.createElement(\"button\");\n btn.className = \"sp-export-btn\";\n btn.setAttribute(\"aria-haspopup\", \"true\");\n btn.setAttribute(\"aria-expanded\", \"false\");\n btn.appendChild(parseSvg(ICON_EXPORT));\n const label = document.createElement(\"span\");\n setText(label, t(\"export.label\"));\n btn.appendChild(label);\n btn.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n this.toggle();\n });\n\n // Dropdown menu\n this.menu = el(\"div\", { class: \"sp-export-menu\" });\n this.menu.setAttribute(\"role\", \"menu\");\n\n // CSV option\n const csvOption = this.createOption(ICON_CSV, t(\"export.csv\"), () => {\n this.exportAs(\"csv\");\n });\n\n // JSON option\n const jsonOption = this.createOption(ICON_JSON, t(\"export.json\"), () => {\n this.exportAs(\"json\");\n });\n\n this.menu.appendChild(csvOption);\n this.menu.appendChild(jsonOption);\n\n this.element.appendChild(btn);\n this.element.appendChild(this.menu);\n\n // Close on outside click\n this.onDocumentClick = (e: MouseEvent) => {\n if (this.isOpen && !this.element.contains(e.target as Node)) {\n this.close();\n }\n };\n document.addEventListener(\"click\", this.onDocumentClick, true);\n }\n\n private createOption(iconSvg: string, labelText: string, onClick: () => void): HTMLButtonElement {\n const option = document.createElement(\"button\");\n option.className = \"sp-export-option\";\n option.setAttribute(\"role\", \"menuitem\");\n\n const iconWrap = el(\"span\", { class: \"sp-export-option-icon\" });\n iconWrap.appendChild(parseSvg(iconSvg));\n\n const labelEl = el(\"span\", { class: \"sp-export-option-label\" });\n setText(labelEl, labelText);\n\n option.appendChild(iconWrap);\n option.appendChild(labelEl);\n\n option.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n onClick();\n this.close();\n });\n\n return option;\n }\n\n private toggle(): void {\n this.isOpen ? this.close() : this.open();\n }\n\n private open(): void {\n this.isOpen = true;\n this.menu.classList.add(\"sp-export-menu--open\");\n const btn = this.element.querySelector<HTMLButtonElement>(\".sp-export-btn\");\n btn?.setAttribute(\"aria-expanded\", \"true\");\n }\n\n private close(): void {\n this.isOpen = false;\n this.menu.classList.remove(\"sp-export-menu--open\");\n const btn = this.element.querySelector<HTMLButtonElement>(\".sp-export-btn\");\n btn?.setAttribute(\"aria-expanded\", \"false\");\n }\n\n private exportAs(format: \"csv\" | \"json\"): void {\n const feedbacks = this.getFeedbacks();\n if (feedbacks.length === 0) return;\n\n const projectName = feedbacks[0]?.projectName ?? \"feedbacks\";\n const date = new Date().toISOString().slice(0, 10);\n const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, \"_\");\n\n if (format === \"csv\") {\n const content = feedbacksToCsv(feedbacks);\n downloadFile(content, `feedbacks-${safeName}-${date}.csv`, \"text/csv;charset=utf-8\");\n } else {\n const content = feedbacksToJson(feedbacks);\n downloadFile(content, `feedbacks-${safeName}-${date}.json`, \"application/json;charset=utf-8\");\n }\n }\n\n destroy(): void {\n document.removeEventListener(\"click\", this.onDocumentClick, true);\n this.element.remove();\n }\n}\n","/**\n * Bulk Actions system for the feedback panel.\n *\n * Allows multi-select on feedback cards with a floating action bar\n * for batch resolve/delete operations. Glassmorphism design with\n * spring animations and smooth transitions.\n */\n\nimport { el, parseSvg, setButtonLoading, setText } from \"./dom-utils.js\";\nimport type { TFunction } from \"./i18n/index.js\";\nimport type { ThemeColors } from \"./styles/theme.js\";\n\n// ---------------------------------------------------------------------------\n// SVG Icons\n// ---------------------------------------------------------------------------\n\nexport const ICON_CHECKBOX = `<svg viewBox=\"0 0 18 18\" fill=\"none\" aria-hidden=\"true\"><rect x=\"1\" y=\"1\" width=\"16\" height=\"16\" rx=\"4\" stroke=\"currentColor\" stroke-width=\"2\"/></svg>`;\n\nexport const ICON_CHECKBOX_CHECKED = `<svg viewBox=\"0 0 18 18\" fill=\"none\" aria-hidden=\"true\"><rect x=\"1\" y=\"1\" width=\"16\" height=\"16\" rx=\"4\" fill=\"url(#sp-cb-grad)\" stroke=\"none\"/><polyline points=\"5 9 8 12 13 6\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/><defs><linearGradient id=\"sp-cb-grad\" x1=\"0\" y1=\"0\" x2=\"18\" y2=\"18\" gradientUnits=\"userSpaceOnUse\"><stop offset=\"0%\" stop-color=\"var(--sp-accent)\"/><stop offset=\"100%\" stop-color=\"var(--sp-accent-dark)\"/></linearGradient></defs></svg>`;\n\n// ---------------------------------------------------------------------------\n// CSS\n// ---------------------------------------------------------------------------\n\nexport const BULK_CSS = `\n /* ============================\n Bulk Checkbox\n ============================ */\n\n .sp-bulk-checkbox {\n position: relative;\n width: 16px;\n height: 16px;\n flex-shrink: 0;\n cursor: pointer;\n border-radius: 4px;\n color: var(--sp-border);\n opacity: 0;\n transition: opacity 0.15s ease, color 0.15s ease, transform 0.15s ease;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n }\n\n .sp-bulk-checkbox svg {\n width: 16px;\n height: 16px;\n display: block;\n }\n\n .sp-bulk-checkbox:hover {\n color: var(--sp-accent);\n transform: scale(1.1);\n }\n\n .sp-bulk-checkbox--checked {\n color: var(--sp-accent);\n opacity: 1 !important;\n filter: drop-shadow(0 0 4px var(--sp-accent-glow));\n }\n\n /* Show checkboxes when hovering a card */\n .sp-card:hover .sp-bulk-checkbox {\n opacity: 1;\n }\n\n /* When any card has selection, show ALL checkboxes */\n .sp-list--has-selection .sp-bulk-checkbox {\n opacity: 1;\n }\n\n /* ============================\n Card Selected State\n ============================ */\n\n .sp-card--selected {\n border-left: 3px solid var(--sp-accent) !important;\n background: var(--sp-accent-light) !important;\n }\n\n .sp-card--selected:hover {\n background: var(--sp-accent-light) !important;\n }\n\n /* ============================\n Select All Bar\n ============================ */\n\n .sp-bulk-select-all {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n margin-bottom: 4px;\n border-radius: var(--sp-radius);\n background: transparent;\n cursor: pointer;\n opacity: 0;\n transition: opacity 0.2s ease, background 0.2s ease;\n user-select: none;\n font-family: var(--sp-font);\n font-size: 12px;\n font-weight: 500;\n color: var(--sp-text-secondary);\n }\n\n .sp-bulk-select-all:hover {\n background: var(--sp-bg-hover);\n }\n\n /* Show select-all on list hover or when selections exist */\n .sp-list:hover .sp-bulk-select-all,\n .sp-list--has-selection .sp-bulk-select-all {\n opacity: 1;\n }\n\n .sp-bulk-select-all .sp-bulk-checkbox {\n opacity: 1;\n }\n\n /* ============================\n Floating Action Bar\n ============================ */\n\n @keyframes sp-bulk-bar-in {\n from {\n transform: translateY(100%) scale(0.95);\n opacity: 0;\n }\n to {\n transform: translateY(0) scale(1);\n opacity: 1;\n }\n }\n\n @keyframes sp-bulk-bar-out {\n from {\n transform: translateY(0) scale(1);\n opacity: 1;\n }\n to {\n transform: translateY(100%) scale(0.95);\n opacity: 0;\n }\n }\n\n .sp-bulk-bar {\n position: absolute;\n bottom: 16px;\n left: 16px;\n right: 16px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n padding: 12px 16px;\n border-radius: 16px;\n background: var(--sp-glass-bg-heavy);\n backdrop-filter: blur(var(--sp-blur-heavy));\n -webkit-backdrop-filter: blur(var(--sp-blur-heavy));\n border: 1px solid var(--sp-glass-border);\n box-shadow: var(--sp-shadow-xl);\n z-index: 10;\n pointer-events: none;\n opacity: 0;\n transform: translateY(100%) scale(0.95);\n transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1),\n opacity 0.25s ease;\n font-family: var(--sp-font);\n }\n\n .sp-bulk-bar--visible {\n pointer-events: auto;\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n\n .sp-bulk-bar-count {\n font-size: 13px;\n font-weight: 600;\n color: var(--sp-text);\n white-space: nowrap;\n letter-spacing: -0.01em;\n }\n\n .sp-bulk-bar-actions {\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n .sp-bulk-btn-resolve,\n .sp-bulk-btn-delete {\n padding: 7px 14px;\n border-radius: var(--sp-radius-full);\n border: 1.5px solid transparent;\n background: transparent;\n font-family: var(--sp-font);\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 5px;\n transition: all 0.2s ease;\n white-space: nowrap;\n }\n\n .sp-bulk-btn-resolve {\n color: #22c55e;\n border-color: #22c55e;\n }\n\n .sp-bulk-btn-resolve:hover {\n background: rgba(34, 197, 94, 0.1);\n box-shadow: 0 0 12px rgba(34, 197, 94, 0.15);\n }\n\n .sp-bulk-btn-resolve:active {\n transform: scale(0.96);\n transition-duration: 0.1s;\n }\n\n .sp-bulk-btn-delete {\n color: #ef4444;\n border-color: #ef4444;\n }\n\n .sp-bulk-btn-delete:hover {\n background: rgba(239, 68, 68, 0.1);\n box-shadow: 0 0 12px rgba(239, 68, 68, 0.15);\n }\n\n .sp-bulk-btn-delete:active {\n transform: scale(0.96);\n transition-duration: 0.1s;\n }\n\n .sp-bulk-btn-resolve:disabled,\n .sp-bulk-btn-delete:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n pointer-events: none;\n }\n\n .sp-bulk-btn-deselect {\n width: 28px;\n height: 28px;\n border-radius: var(--sp-radius-full);\n border: 1px solid var(--sp-border);\n background: transparent;\n color: var(--sp-text-tertiary);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s ease;\n flex-shrink: 0;\n padding: 0;\n }\n\n .sp-bulk-btn-deselect:hover {\n background: var(--sp-bg-hover);\n color: var(--sp-text);\n border-color: var(--sp-text-tertiary);\n }\n\n .sp-bulk-btn-deselect:active {\n transform: scale(0.92);\n transition-duration: 0.1s;\n }\n\n .sp-bulk-btn-deselect svg {\n width: 12px;\n height: 12px;\n }\n\n /* Spinner inside bulk bar buttons */\n .sp-bulk-btn-resolve .sp-spinner,\n .sp-bulk-btn-delete .sp-spinner {\n width: 14px;\n height: 14px;\n }\n\n /* ============================\n Forced Colors / High Contrast\n ============================ */\n\n @media (forced-colors: active) {\n .sp-bulk-checkbox,\n .sp-bulk-btn-resolve,\n .sp-bulk-btn-delete,\n .sp-bulk-btn-deselect,\n .sp-bulk-bar {\n border: 2px solid ButtonText !important;\n background: Canvas !important;\n color: ButtonText !important;\n }\n\n .sp-bulk-checkbox--checked {\n background: Highlight !important;\n color: HighlightText !important;\n }\n\n .sp-card--selected {\n border-left: 4px solid Highlight !important;\n }\n }\n\n /* ============================\n Reduced Motion\n ============================ */\n\n @media (prefers-reduced-motion: reduce) {\n .sp-bulk-bar {\n transition-duration: 0.01ms !important;\n }\n\n .sp-bulk-checkbox {\n transition-duration: 0.01ms !important;\n }\n }\n`;\n\n// ---------------------------------------------------------------------------\n// Callbacks\n// ---------------------------------------------------------------------------\n\nexport interface BulkActionCallbacks {\n onResolve: (ids: string[]) => Promise<void>;\n onDelete: (ids: string[]) => Promise<void>;\n}\n\n// ---------------------------------------------------------------------------\n// BulkActions Class\n// ---------------------------------------------------------------------------\n\nexport class BulkActions {\n /** The floating action bar element — append to panel root */\n readonly barElement: HTMLElement;\n\n private selected = new Set<string>();\n private checkboxMap = new Map<string, HTMLElement>();\n private countLabel: HTMLElement;\n private resolveBtn: HTMLButtonElement;\n private deleteBtn: HTMLButtonElement;\n private selectAllCheckbox: HTMLElement | null = null;\n private listContainer: HTMLElement | null = null;\n private isProcessing = false;\n private readonly t: TFunction;\n\n constructor(\n _colors: ThemeColors,\n private readonly callbacks: BulkActionCallbacks,\n t: TFunction,\n ) {\n this.t = t;\n // ----- Build floating bar -----\n this.barElement = el(\"div\", { class: \"sp-bulk-bar\" });\n this.barElement.setAttribute(\"role\", \"toolbar\");\n this.barElement.setAttribute(\"aria-label\", \"Bulk actions\");\n\n // Left: count label\n this.countLabel = el(\"span\", { class: \"sp-bulk-bar-count\" });\n setText(this.countLabel, this.t(\"bulk.selected\").replace(\"{count}\", \"0\"));\n\n // Right: action buttons\n const actions = el(\"div\", { class: \"sp-bulk-bar-actions\" });\n\n this.resolveBtn = document.createElement(\"button\");\n this.resolveBtn.className = \"sp-bulk-btn-resolve\";\n this.resolveBtn.type = \"button\";\n this.resolveBtn.addEventListener(\"click\", () => this.handleResolve());\n\n this.deleteBtn = document.createElement(\"button\");\n this.deleteBtn.className = \"sp-bulk-btn-delete\";\n this.deleteBtn.type = \"button\";\n this.deleteBtn.addEventListener(\"click\", () => this.handleDelete());\n\n const deselectBtn = document.createElement(\"button\");\n deselectBtn.className = \"sp-bulk-btn-deselect\";\n deselectBtn.type = \"button\";\n deselectBtn.setAttribute(\"aria-label\", this.t(\"bulk.deselect\"));\n deselectBtn.appendChild(\n parseSvg(\n `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/></svg>`,\n ),\n );\n deselectBtn.addEventListener(\"click\", () => this.deselectAll());\n\n actions.appendChild(this.resolveBtn);\n actions.appendChild(this.deleteBtn);\n actions.appendChild(deselectBtn);\n\n this.barElement.appendChild(this.countLabel);\n this.barElement.appendChild(actions);\n\n // Initial button text\n this.updateButtonLabels();\n }\n\n /** Create a checkbox element for a card. Returns the checkbox wrapper. */\n createCheckbox(feedbackId: string): HTMLElement {\n const wrapper = el(\"div\", { class: \"sp-bulk-checkbox\" });\n wrapper.setAttribute(\"role\", \"checkbox\");\n wrapper.setAttribute(\"aria-checked\", \"false\");\n wrapper.setAttribute(\"tabindex\", \"0\");\n wrapper.setAttribute(\"aria-label\", `Select feedback ${feedbackId}`);\n\n // Render unchecked icon\n wrapper.appendChild(parseSvg(ICON_CHECKBOX));\n\n // Click handler\n wrapper.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n this.toggle(feedbackId);\n });\n\n // Keyboard: space/enter to toggle\n wrapper.addEventListener(\"keydown\", (e) => {\n if ((e as KeyboardEvent).key === \" \" || (e as KeyboardEvent).key === \"Enter\") {\n e.preventDefault();\n e.stopPropagation();\n this.toggle(feedbackId);\n }\n });\n\n this.checkboxMap.set(feedbackId, wrapper);\n return wrapper;\n }\n\n /**\n * Create a \"Select all\" bar element.\n * The caller should insert this at the top of the list container.\n */\n createSelectAllBar(feedbackIds: string[], label: string): HTMLElement {\n const wrapper = el(\"div\", { class: \"sp-bulk-select-all\" });\n\n const checkbox = el(\"div\", { class: \"sp-bulk-checkbox\" });\n checkbox.appendChild(parseSvg(ICON_CHECKBOX));\n this.selectAllCheckbox = checkbox;\n\n const labelEl = el(\"span\");\n setText(labelEl, label);\n\n wrapper.appendChild(checkbox);\n wrapper.appendChild(labelEl);\n\n wrapper.addEventListener(\"click\", () => {\n // If all selected, deselect; otherwise select all\n if (this.selected.size === feedbackIds.length && feedbackIds.length > 0) {\n this.deselectAll();\n } else {\n this.selectAll(feedbackIds);\n }\n });\n\n return wrapper;\n }\n\n /** Set the list container reference (for toggling .sp-list--has-selection) */\n setListContainer(container: HTMLElement): void {\n this.listContainer = container;\n }\n\n /** Toggle selection for a feedback */\n toggle(feedbackId: string): void {\n if (this.isProcessing) return;\n\n if (this.selected.has(feedbackId)) {\n this.selected.delete(feedbackId);\n } else {\n this.selected.add(feedbackId);\n }\n this.updateCheckbox(feedbackId);\n this.updateBar();\n this.updateSelectAllCheckbox();\n this.updateListSelectionClass();\n this.updateCardSelectedState(feedbackId);\n }\n\n /** Select all from the given list */\n selectAll(feedbackIds: string[]): void {\n if (this.isProcessing) return;\n\n for (const id of feedbackIds) {\n this.selected.add(id);\n this.updateCheckbox(id);\n this.updateCardSelectedState(id);\n }\n this.updateBar();\n this.updateSelectAllCheckbox();\n this.updateListSelectionClass();\n }\n\n /** Clear all selections */\n deselectAll(): void {\n const prevSelected = [...this.selected];\n this.selected.clear();\n for (const id of prevSelected) {\n this.updateCheckbox(id);\n this.updateCardSelectedState(id);\n }\n this.updateBar();\n this.updateSelectAllCheckbox();\n this.updateListSelectionClass();\n }\n\n /** Get currently selected IDs */\n get selectedIds(): string[] {\n return [...this.selected];\n }\n\n /** Get selection count */\n get count(): number {\n return this.selected.size;\n }\n\n /** Whether any items are selected */\n get hasSelection(): boolean {\n return this.selected.size > 0;\n }\n\n /** Reset state (e.g., after feedbacks reload) */\n reset(): void {\n this.selected.clear();\n this.checkboxMap.clear();\n this.selectAllCheckbox = null;\n\n this.isProcessing = false;\n this.updateBar();\n this.updateListSelectionClass();\n }\n\n /** Destroy / cleanup */\n destroy(): void {\n this.selected.clear();\n this.checkboxMap.clear();\n this.selectAllCheckbox = null;\n\n this.listContainer = null;\n this.barElement.remove();\n }\n\n // -----------------------------------------------------------------------\n // Private\n // -----------------------------------------------------------------------\n\n /** Update the bar visibility and counts */\n private updateBar(): void {\n const count = this.selected.size;\n const visible = count > 0;\n\n this.barElement.classList.toggle(\"sp-bulk-bar--visible\", visible);\n setText(this.countLabel, this.t(\"bulk.selected\").replace(\"{count}\", String(count)));\n this.updateButtonLabels();\n }\n\n private updateButtonLabels(): void {\n const count = this.selected.size;\n const resolve = this.t(\"bulk.resolve\");\n const del = this.t(\"bulk.delete\");\n\n // Resolve button\n this.resolveBtn.replaceChildren();\n const resolveLabel = document.createElement(\"span\");\n setText(resolveLabel, count > 0 ? `${resolve} ${count}` : resolve);\n this.resolveBtn.appendChild(resolveLabel);\n\n // Delete button\n this.deleteBtn.replaceChildren();\n const deleteLabel = document.createElement(\"span\");\n setText(deleteLabel, count > 0 ? `${del} ${count}` : del);\n this.deleteBtn.appendChild(deleteLabel);\n }\n\n private updateCheckbox(feedbackId: string): void {\n const checkbox = this.checkboxMap.get(feedbackId);\n if (!checkbox) return;\n\n const isChecked = this.selected.has(feedbackId);\n checkbox.classList.toggle(\"sp-bulk-checkbox--checked\", isChecked);\n checkbox.setAttribute(\"aria-checked\", String(isChecked));\n\n // Swap SVG icon\n checkbox.replaceChildren();\n checkbox.appendChild(parseSvg(isChecked ? ICON_CHECKBOX_CHECKED : ICON_CHECKBOX));\n }\n\n private updateSelectAllCheckbox(): void {\n if (!this.selectAllCheckbox) return;\n\n const allSelected = this.selected.size > 0 && this.selected.size === this.checkboxMap.size;\n this.selectAllCheckbox.classList.toggle(\"sp-bulk-checkbox--checked\", allSelected);\n this.selectAllCheckbox.setAttribute(\"aria-checked\", String(allSelected));\n\n this.selectAllCheckbox.replaceChildren();\n this.selectAllCheckbox.appendChild(parseSvg(allSelected ? ICON_CHECKBOX_CHECKED : ICON_CHECKBOX));\n }\n\n private updateListSelectionClass(): void {\n if (!this.listContainer) return;\n this.listContainer.classList.toggle(\"sp-list--has-selection\", this.selected.size > 0);\n }\n\n private updateCardSelectedState(feedbackId: string): void {\n if (!this.listContainer) return;\n const escapedId = CSS.escape(feedbackId);\n const card = this.listContainer.querySelector<HTMLElement>(`[data-feedback-id=\"${escapedId}\"]`);\n if (card) {\n card.classList.toggle(\"sp-card--selected\", this.selected.has(feedbackId));\n }\n }\n\n private async handleResolve(): Promise<void> {\n if (this.isProcessing || this.selected.size === 0) return;\n this.isProcessing = true;\n\n const ids = [...this.selected];\n const restoreResolve = setButtonLoading(this.resolveBtn);\n this.deleteBtn.disabled = true;\n\n try {\n await this.callbacks.onResolve(ids);\n this.reset();\n } catch {\n restoreResolve();\n this.deleteBtn.disabled = false;\n } finally {\n this.isProcessing = false;\n }\n }\n\n private async handleDelete(): Promise<void> {\n if (this.isProcessing || this.selected.size === 0) return;\n this.isProcessing = true;\n\n const ids = [...this.selected];\n const restoreDelete = setButtonLoading(this.deleteBtn);\n this.resolveBtn.disabled = true;\n\n try {\n await this.callbacks.onDelete(ids);\n this.reset();\n } catch {\n restoreDelete();\n this.resolveBtn.disabled = false;\n } finally {\n this.isProcessing = false;\n }\n }\n}\n","/**\n * Detail View for the feedback panel.\n *\n * Slides in from the right (panel-in-panel pattern) when a user clicks\n * a feedback card, showing full details: message, metadata, annotation\n * info, status actions, and a \"Go to annotation\" button.\n *\n * Glassmorphism design — frosted glass surfaces, staggered section\n * animations, accent gradients, premium micro-interactions.\n */\n\nimport type { FeedbackResponse } from \"@siteping/core\";\nimport { el, parseSvg, setText } from \"./dom-utils.js\";\nimport type { TFunction } from \"./i18n/index.js\";\nimport { getTypeBgColor, getTypeColor, type ThemeColors } from \"./styles/theme.js\";\n\n// ---------------------------------------------------------------------------\n// SVG Icons\n// ---------------------------------------------------------------------------\n\nexport const ICON_ARROW_LEFT = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><line x1=\"19\" y1=\"12\" x2=\"5\" y2=\"12\"/><polyline points=\"12 19 5 12 12 5\"/></svg>`;\n\nexport const ICON_MAP_PIN = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z\"/><circle cx=\"12\" cy=\"10\" r=\"3\"/></svg>`;\n\nexport const ICON_LINK = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"/><path d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"/></svg>`;\n\nexport const ICON_USER = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2\"/><circle cx=\"12\" cy=\"7\" r=\"4\"/></svg>`;\n\nexport const ICON_CALENDAR = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\" ry=\"2\"/><line x1=\"16\" y1=\"2\" x2=\"16\" y2=\"6\"/><line x1=\"8\" y1=\"2\" x2=\"8\" y2=\"6\"/><line x1=\"3\" y1=\"10\" x2=\"21\" y2=\"10\"/></svg>`;\n\nexport const ICON_MONITOR = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><rect x=\"2\" y=\"3\" width=\"20\" height=\"14\" rx=\"2\" ry=\"2\"/><line x1=\"8\" y1=\"21\" x2=\"16\" y2=\"21\"/><line x1=\"12\" y1=\"17\" x2=\"12\" y2=\"21\"/></svg>`;\n\nconst ICON_CHECK = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><polyline points=\"20 6 9 17 4 12\"/></svg>`;\n\nconst ICON_UNDO = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><polyline points=\"1 4 1 10 7 10\"/><path d=\"M3.51 15a9 9 0 1 0 2.13-9.36L1 10\"/></svg>`;\n\nconst ICON_TRASH = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><polyline points=\"3 6 5 6 21 6\"/><path d=\"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\"/><line x1=\"10\" y1=\"11\" x2=\"10\" y2=\"17\"/><line x1=\"14\" y1=\"11\" x2=\"14\" y2=\"17\"/></svg>`;\n\nconst ICON_CODE = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><polyline points=\"16 18 22 12 16 6\"/><polyline points=\"8 6 2 12 8 18\"/></svg>`;\n\nconst ICON_CROSSHAIR = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"22\" y1=\"12\" x2=\"18\" y2=\"12\"/><line x1=\"6\" y1=\"12\" x2=\"2\" y2=\"12\"/><line x1=\"12\" y1=\"6\" x2=\"12\" y2=\"2\"/><line x1=\"12\" y1=\"22\" x2=\"12\" y2=\"18\"/></svg>`;\n\nconst ICON_CHEVRON = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><polyline points=\"9 18 15 12 9 6\"/></svg>`;\nconst ICON_TERMINAL = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><polyline points=\"4 17 10 11 4 5\"/><line x1=\"12\" y1=\"19\" x2=\"20\" y2=\"19\"/></svg>`;\n\n// ---------------------------------------------------------------------------\n// CSS\n// ---------------------------------------------------------------------------\n\nexport const DETAIL_CSS = /* css */ `\n /* ============================\n Detail View — Panel-in-Panel\n ============================ */\n\n .sp-detail {\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n background: var(--sp-glass-bg);\n backdrop-filter: blur(var(--sp-blur-heavy));\n -webkit-backdrop-filter: blur(var(--sp-blur-heavy));\n z-index: 20;\n transform: translateX(100%);\n transition: transform 0.35s cubic-bezier(0.16, 1, 0.3, 1);\n will-change: transform;\n overflow: hidden;\n }\n\n .sp-detail--visible {\n transform: translateX(0);\n }\n\n /* Fallback for browsers that cannot deliver a readable \"frosted glass\":\n drop the translucent background to a solid one so the underlying list\n does not bleed through. Two disjoint cohorts:\n\n 1. No backdrop-filter at all (Firefox <=102, legacy Edge / IE,\n older Chromium on Linux).\n 2. Safari / iOS Safari where backdrop-filter is detectable only\n via the -webkit- prefix. Empirically this still includes recent\n Safari (observed on macOS Safari 18.6 in 2026, where\n CSS.supports('backdrop-filter', 'blur(...)') returns false even\n though the unprefixed property has shipped). On these builds the\n long-standing nested-backdrop + transform compositing bug\n silently no-ops the blur on .sp-detail (which is transformed and\n lives inside another backdrop-filter ancestor, .sp-panel), so\n the translucent default is unreadable. Detection is a pure\n feature query: prefixed supported AND unprefixed not. No\n user-agent sniffing.\n\n Browsers where the glass effect renders correctly (most Chromium,\n modern Firefox, any engine that advertises both property names via\n CSS.supports) are unaffected. */\n @supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {\n .sp-detail {\n background: var(--sp-bg);\n }\n }\n\n @supports (-webkit-backdrop-filter: blur(1px)) and (not (backdrop-filter: blur(1px))) {\n .sp-detail {\n background: var(--sp-bg);\n }\n }\n\n /* ---- Header ---- */\n\n .sp-detail-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 16px 20px;\n border-bottom: 1px solid var(--sp-border);\n background: var(--sp-glass-bg-heavy);\n backdrop-filter: blur(var(--sp-blur));\n -webkit-backdrop-filter: blur(var(--sp-blur));\n flex-shrink: 0;\n min-height: 64px;\n }\n\n .sp-detail-back {\n width: 40px;\n height: 40px;\n border-radius: var(--sp-radius);\n border: none;\n background: transparent;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--sp-text-tertiary);\n transition: all 0.2s ease;\n flex-shrink: 0;\n padding: 0;\n }\n\n .sp-detail-back:hover {\n background: var(--sp-bg-hover);\n color: var(--sp-text);\n }\n\n .sp-detail-back:active {\n transform: scale(0.92);\n transition-duration: 0.1s;\n }\n\n .sp-detail-back svg {\n width: 18px;\n height: 18px;\n }\n\n .sp-detail-title {\n font-size: 16px;\n font-weight: 700;\n color: var(--sp-text);\n letter-spacing: -0.02em;\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .sp-detail-header .sp-badge {\n flex-shrink: 0;\n }\n\n /* ---- Content ---- */\n\n .sp-detail-content {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n padding: 0;\n }\n\n .sp-detail-content::-webkit-scrollbar {\n width: 6px;\n }\n\n .sp-detail-content::-webkit-scrollbar-track {\n background: transparent;\n }\n\n .sp-detail-content::-webkit-scrollbar-thumb {\n background: var(--sp-border);\n border-radius: var(--sp-radius-full);\n }\n\n .sp-detail-content::-webkit-scrollbar-thumb:hover {\n background: var(--sp-text-tertiary);\n }\n\n /* ---- Section ---- */\n\n .sp-detail-section {\n padding: 20px 24px;\n border-bottom: 1px solid var(--sp-border);\n opacity: 0;\n transform: translateY(8px);\n animation: sp-detail-section-in 0.35s cubic-bezier(0.16, 1, 0.3, 1) forwards;\n }\n\n @keyframes sp-detail-section-in {\n to {\n opacity: 1;\n transform: translateY(0);\n }\n }\n\n .sp-detail-section:last-child {\n border-bottom: none;\n }\n\n .sp-detail-section-title {\n font-size: 11px;\n font-weight: 600;\n color: var(--sp-text-tertiary);\n text-transform: uppercase;\n letter-spacing: 0.06em;\n margin-bottom: 14px;\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n .sp-detail-section-title svg {\n width: 14px;\n height: 14px;\n opacity: 0.6;\n }\n\n /* ---- Status + Actions Section ---- */\n\n .sp-detail-status {\n display: flex;\n align-items: center;\n gap: 10px;\n margin-bottom: 16px;\n }\n\n .sp-detail-status-pill {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 5px 14px;\n border-radius: var(--sp-radius-full);\n font-size: 12px;\n font-weight: 600;\n letter-spacing: 0.02em;\n }\n\n .sp-detail-status-pill--open {\n background: rgba(34, 197, 94, 0.1);\n color: #22c55e;\n border: 1px solid rgba(34, 197, 94, 0.2);\n }\n\n .sp-detail-status-pill--resolved {\n background: rgba(156, 163, 175, 0.1);\n color: #9ca3af;\n border: 1px solid rgba(156, 163, 175, 0.2);\n }\n\n .sp-detail-status-dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n flex-shrink: 0;\n }\n\n .sp-detail-actions {\n display: flex;\n gap: 8px;\n }\n\n .sp-detail-actions button {\n flex: 1;\n height: 40px;\n padding: 0 16px;\n border-radius: var(--sp-radius);\n font-family: var(--sp-font);\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n transition: all 0.2s ease;\n }\n\n .sp-detail-actions button svg {\n width: 15px;\n height: 15px;\n }\n\n .sp-detail-btn-resolve {\n border: 1.5px solid #22c55e;\n background: rgba(34, 197, 94, 0.06);\n color: #22c55e;\n }\n\n .sp-detail-btn-resolve:hover {\n background: rgba(34, 197, 94, 0.14);\n box-shadow: 0 0 16px rgba(34, 197, 94, 0.12);\n transform: translateY(-1px);\n }\n\n .sp-detail-btn-resolve:active {\n transform: translateY(0) scale(0.98);\n transition-duration: 0.1s;\n }\n\n .sp-detail-btn-reopen {\n border: 1.5px solid var(--sp-accent);\n background: var(--sp-accent-light);\n color: var(--sp-accent);\n }\n\n .sp-detail-btn-reopen:hover {\n background: rgba(var(--sp-accent), 0.14);\n box-shadow: 0 0 16px var(--sp-accent-glow);\n transform: translateY(-1px);\n }\n\n .sp-detail-btn-reopen:active {\n transform: translateY(0) scale(0.98);\n transition-duration: 0.1s;\n }\n\n .sp-detail-btn-delete {\n border: 1.5px solid #ef4444;\n background: rgba(239, 68, 68, 0.06);\n color: #ef4444;\n }\n\n .sp-detail-btn-delete:hover {\n background: rgba(239, 68, 68, 0.14);\n box-shadow: 0 0 16px rgba(239, 68, 68, 0.12);\n transform: translateY(-1px);\n }\n\n .sp-detail-btn-delete:active {\n transform: translateY(0) scale(0.98);\n transition-duration: 0.1s;\n }\n\n .sp-detail-actions button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n pointer-events: none;\n transform: none;\n box-shadow: none;\n }\n\n /* ---- Message Section ---- */\n\n .sp-detail-message {\n font-size: 14px;\n line-height: 1.65;\n color: var(--sp-text);\n padding: 14px 16px;\n border-left: 3px solid var(--sp-accent);\n border-radius: 0 var(--sp-radius) var(--sp-radius) 0;\n background: var(--sp-glass-bg-heavy);\n white-space: pre-wrap;\n word-break: break-word;\n }\n\n /* ---- Screenshot Section ---- */\n\n .sp-detail-screenshot {\n display: block;\n width: 100%;\n height: auto;\n max-height: 400px;\n object-fit: contain;\n border-radius: var(--sp-radius);\n border: 1px solid var(--sp-glass-border);\n background: var(--sp-glass-bg-heavy);\n }\n\n /* ---- Metadata Section ---- */\n\n .sp-detail-meta {\n display: flex;\n flex-direction: column;\n gap: 14px;\n }\n\n .sp-detail-meta-row {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n }\n\n .sp-detail-meta-row svg {\n width: 14px;\n height: 14px;\n color: var(--sp-text-tertiary);\n flex-shrink: 0;\n margin-top: 1px;\n }\n\n .sp-detail-meta-content {\n flex: 1;\n min-width: 0;\n }\n\n .sp-detail-meta-label {\n font-size: 10px;\n font-weight: 600;\n color: var(--sp-text-tertiary);\n text-transform: uppercase;\n letter-spacing: 0.06em;\n line-height: 1;\n margin-bottom: 4px;\n }\n\n .sp-detail-meta-value {\n font-size: 13px;\n line-height: 1.4;\n color: var(--sp-text);\n word-break: break-all;\n }\n\n .sp-detail-meta-value--mono {\n font-family: \"SF Mono\", \"Cascadia Code\", \"Fira Code\", \"Consolas\", monospace;\n font-size: 12px;\n background: var(--sp-glass-bg-heavy);\n padding: 2px 6px;\n border-radius: 4px;\n border: 1px solid var(--sp-glass-border-subtle);\n }\n\n .sp-detail-meta-value--secondary {\n color: var(--sp-text-secondary);\n font-size: 12px;\n }\n\n /* ---- Annotation Section ---- */\n\n .sp-detail-annotation {\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n\n .sp-detail-annotation-info {\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 14px;\n border-radius: var(--sp-radius);\n background: var(--sp-glass-bg-heavy);\n border: 1px solid var(--sp-glass-border-subtle);\n }\n\n .sp-detail-annotation-row {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n }\n\n .sp-detail-annotation-row svg {\n width: 13px;\n height: 13px;\n color: var(--sp-text-tertiary);\n flex-shrink: 0;\n margin-top: 2px;\n }\n\n .sp-detail-annotation-label {\n font-size: 10px;\n font-weight: 600;\n color: var(--sp-text-tertiary);\n text-transform: uppercase;\n letter-spacing: 0.06em;\n line-height: 1;\n margin-bottom: 3px;\n }\n\n .sp-detail-annotation-value {\n font-size: 12px;\n line-height: 1.4;\n color: var(--sp-text);\n word-break: break-all;\n }\n\n .sp-detail-annotation-value--mono {\n font-family: \"SF Mono\", \"Cascadia Code\", \"Fira Code\", \"Consolas\", monospace;\n font-size: 11px;\n background: var(--sp-bg-hover);\n padding: 2px 6px;\n border-radius: 4px;\n display: inline-block;\n max-width: 100%;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .sp-detail-btn-goto {\n width: 100%;\n height: 44px;\n padding: 0 20px;\n border-radius: var(--sp-radius);\n border: none;\n background: var(--sp-accent-gradient);\n color: #fff;\n font-family: var(--sp-font);\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n transition: all 0.25s ease;\n box-shadow: 0 2px 12px var(--sp-accent-glow);\n }\n\n .sp-detail-btn-goto svg {\n width: 16px;\n height: 16px;\n }\n\n .sp-detail-btn-goto:hover {\n box-shadow: 0 4px 20px var(--sp-accent-glow);\n transform: translateY(-2px);\n }\n\n .sp-detail-btn-goto:active {\n transform: translateY(0) scale(0.98);\n transition-duration: 0.1s;\n }\n\n /* ---- Forced Colors / High Contrast ---- */\n\n @media (forced-colors: active) {\n .sp-detail {\n border: 2px solid ButtonText !important;\n background: Canvas !important;\n }\n\n .sp-detail-back,\n .sp-detail-btn-goto,\n .sp-detail-btn-resolve,\n .sp-detail-btn-reopen,\n .sp-detail-btn-delete {\n border: 2px solid ButtonText !important;\n background: Canvas !important;\n color: ButtonText !important;\n }\n\n .sp-detail-back:focus-visible,\n .sp-detail-btn-goto:focus-visible,\n .sp-detail-btn-resolve:focus-visible,\n .sp-detail-btn-reopen:focus-visible,\n .sp-detail-btn-delete:focus-visible {\n outline: 3px solid Highlight !important;\n }\n\n .sp-detail-status-pill {\n border: 2px solid ButtonText !important;\n background: Canvas !important;\n color: ButtonText !important;\n }\n\n .sp-detail-message {\n border-left: 3px solid ButtonText !important;\n }\n }\n\n /* ---- Diagnostics Section ---- */\n\n .sp-detail-diag {\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n\n .sp-detail-diag-toggle {\n width: 100%;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n padding: 10px 12px;\n border-radius: var(--sp-radius);\n border: 1px solid var(--sp-glass-border-subtle);\n background: var(--sp-glass-bg-heavy);\n color: var(--sp-text);\n font-family: var(--sp-font);\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n transition: background 0.15s ease;\n }\n\n .sp-detail-diag-toggle:hover {\n background: var(--sp-bg-hover);\n }\n\n .sp-detail-diag-toggle svg {\n width: 12px;\n height: 12px;\n transition: transform 0.2s ease;\n }\n\n .sp-detail-diag-toggle[aria-expanded=\"true\"] svg {\n transform: rotate(90deg);\n }\n\n .sp-detail-diag-counts {\n display: inline-flex;\n gap: 6px;\n font-weight: 500;\n color: var(--sp-text-tertiary);\n }\n\n .sp-detail-diag-count {\n padding: 1px 7px;\n border-radius: var(--sp-radius-full);\n background: var(--sp-bg-hover);\n font-variant-numeric: tabular-nums;\n }\n\n .sp-detail-diag-count--errors {\n background: rgba(239, 68, 68, 0.14);\n color: #ef4444;\n }\n\n .sp-detail-diag-body {\n display: none;\n flex-direction: column;\n gap: 14px;\n }\n\n .sp-detail-diag-body--open {\n display: flex;\n }\n\n .sp-detail-diag-group-title {\n font-size: 10px;\n font-weight: 700;\n color: var(--sp-text-tertiary);\n text-transform: uppercase;\n letter-spacing: 0.06em;\n margin-bottom: 6px;\n }\n\n .sp-detail-diag-list {\n list-style: none;\n padding: 0;\n margin: 0;\n border-radius: var(--sp-radius);\n border: 1px solid var(--sp-glass-border-subtle);\n background: var(--sp-glass-bg-heavy);\n max-height: 240px;\n overflow-y: auto;\n }\n\n .sp-detail-diag-list li {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n padding: 8px 10px;\n border-bottom: 1px solid var(--sp-glass-border-subtle);\n font-family: \"SF Mono\", \"Cascadia Code\", \"Fira Code\", \"Consolas\", monospace;\n font-size: 11px;\n line-height: 1.45;\n color: var(--sp-text);\n }\n\n .sp-detail-diag-list li:last-child {\n border-bottom: none;\n }\n\n .sp-detail-diag-level {\n flex-shrink: 0;\n font-weight: 700;\n width: 44px;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n font-size: 10px;\n }\n\n .sp-detail-diag-level--log {\n color: var(--sp-text-tertiary);\n }\n .sp-detail-diag-level--info {\n color: #3b82f6;\n }\n .sp-detail-diag-level--warn {\n color: #f59e0b;\n }\n .sp-detail-diag-level--error {\n color: #ef4444;\n }\n\n .sp-detail-diag-message {\n flex: 1;\n min-width: 0;\n word-break: break-word;\n white-space: pre-wrap;\n }\n\n .sp-detail-diag-net {\n display: grid;\n grid-template-columns: auto 1fr auto;\n gap: 8px;\n align-items: center;\n }\n\n .sp-detail-diag-net-status {\n flex-shrink: 0;\n font-weight: 700;\n color: #ef4444;\n min-width: 32px;\n text-align: right;\n font-variant-numeric: tabular-nums;\n }\n\n .sp-detail-diag-net-method {\n flex-shrink: 0;\n font-weight: 600;\n color: var(--sp-text-tertiary);\n min-width: 44px;\n }\n\n .sp-detail-diag-net-url {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--sp-text);\n }\n\n .sp-detail-diag-empty {\n padding: 12px;\n font-style: italic;\n font-size: 11px;\n color: var(--sp-text-tertiary);\n text-align: center;\n }\n\n /* ---- Reduced Motion ---- */\n\n @media (prefers-reduced-motion: reduce) {\n .sp-detail {\n transition-duration: 0.01ms !important;\n }\n\n .sp-detail-section {\n animation-duration: 0.01ms !important;\n }\n }\n`;\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Parse a userAgent string to extract the browser name and version. */\nfunction parseBrowser(ua: string): string {\n // Order matters: Edge includes \"Edg/\", Chrome includes \"Chrome/\", etc.\n if (/Edg\\//i.test(ua)) {\n const m = ua.match(/Edg\\/([\\d.]+)/);\n return m ? `Edge ${m[1]}` : \"Edge\";\n }\n if (/OPR\\//i.test(ua) || /Opera/i.test(ua)) {\n const m = ua.match(/OPR\\/([\\d.]+)/);\n return m ? `Opera ${m[1]}` : \"Opera\";\n }\n if (/Firefox\\//i.test(ua)) {\n const m = ua.match(/Firefox\\/([\\d.]+)/);\n return m ? `Firefox ${m[1]}` : \"Firefox\";\n }\n if (/Chrome\\//i.test(ua) && !/Chromium/i.test(ua)) {\n const m = ua.match(/Chrome\\/([\\d.]+)/);\n return m ? `Chrome ${m[1]}` : \"Chrome\";\n }\n if (/Safari\\//i.test(ua) && !/Chrome/i.test(ua)) {\n const m = ua.match(/Version\\/([\\d.]+)/);\n return m ? `Safari ${m[1]}` : \"Safari\";\n }\n return \"Unknown\";\n}\n\n/** Format an ISO date string to a full locale-aware date/time. */\nfunction formatFullDate(isoString: string, locale: string): string {\n try {\n const d = new Date(isoString);\n return d.toLocaleString(locale, {\n year: \"numeric\",\n month: \"long\",\n day: \"numeric\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n });\n } catch {\n return isoString;\n }\n}\n\n/** Extract the pathname from a URL string, falling back to the raw string. */\nfunction extractPathname(url: string): string {\n try {\n return new URL(url).pathname;\n } catch {\n return url;\n }\n}\n\n/**\n * Whitelist URL schemes that are safe to use as `<img src>`. Defends against\n * a buggy or compromised `ScreenshotStorage` (or DB) writing a `javascript:`,\n * `data:text/html`, or `data:image/svg+xml` URL — the latter can host\n * external `<image href>` references that exfiltrate IP/UA/Referer when the\n * panel renders. Browsers refuse to execute scripts via `<img>`, but the\n * fetch itself still happens for arbitrary URLs.\n */\nfunction isSafeImageUrl(url: string): boolean {\n // data:image/(jpeg|png|webp) is what the widget produces; SVG is excluded\n // because it can contain external references and is rarely a useful\n // screenshot format.\n if (/^data:image\\/(jpeg|png|webp);/i.test(url)) return true;\n // Remote URLs over https are accepted (S3, R2, etc.). http: is rejected\n // because the panel typically runs over https and mixed-content is blocked\n // anyway — surfacing the issue here is clearer than a silent network error.\n if (/^https:\\/\\//i.test(url)) return true;\n return false;\n}\n\n/** Truncate a string to a max length with ellipsis. */\nfunction truncate(str: string, max: number): string {\n if (str.length <= max) return str;\n return str.slice(0, max - 1) + \"\\u2026\";\n}\n\n/**\n * Whether a feedback carries a usable diagnostics payload. We only render the\n * section when at least one channel has entries; the section title alone with\n * two empty lists is just noise.\n */\nfunction hasDiagnostics(diagnostics: FeedbackResponse[\"diagnostics\"]): boolean {\n if (!diagnostics) return false;\n const consoleLen = Array.isArray(diagnostics.console) ? diagnostics.console.length : 0;\n const networkLen = Array.isArray(diagnostics.network) ? diagnostics.network.length : 0;\n return consoleLen > 0 || networkLen > 0;\n}\n\n/** Format a duration in ms as a compact \"123 ms\" / \"1.4 s\" string. */\nfunction formatDuration(ms: number): string {\n if (!Number.isFinite(ms) || ms < 0) return \"\\u2014\";\n if (ms < 1000) return `${Math.round(ms)} ms`;\n return `${(ms / 1000).toFixed(1)} s`;\n}\n\n// ---------------------------------------------------------------------------\n// Callbacks\n// ---------------------------------------------------------------------------\n\nexport interface DetailCallbacks {\n onBack: () => void;\n onResolve: (feedback: FeedbackResponse) => Promise<void>;\n onDelete: (feedback: FeedbackResponse) => Promise<void>;\n onGoToAnnotation: (feedback: FeedbackResponse) => void;\n}\n\n// ---------------------------------------------------------------------------\n// DetailView Class\n// ---------------------------------------------------------------------------\n\nexport class DetailView {\n readonly element: HTMLElement;\n\n private _isVisible = false;\n private currentFeedback: FeedbackResponse | null = null;\n private readonly content: HTMLElement;\n private readonly t: TFunction;\n private readonly locale: string;\n private resolveBtn: HTMLButtonElement | null = null;\n private deleteBtn: HTMLButtonElement | null = null;\n private isProcessing = false;\n\n constructor(\n private readonly colors: ThemeColors,\n private readonly callbacks: DetailCallbacks,\n t: TFunction,\n locale: string,\n ) {\n this.t = t;\n this.locale = locale;\n\n // Root container\n this.element = el(\"div\", { class: \"sp-detail\" });\n this.element.setAttribute(\"role\", \"dialog\");\n this.element.setAttribute(\"aria-label\", \"Feedback detail\");\n this.element.setAttribute(\"aria-hidden\", \"true\");\n\n // Header (built once, title/badge updated on show())\n const header = el(\"div\", { class: \"sp-detail-header\" });\n\n const backBtn = document.createElement(\"button\");\n backBtn.type = \"button\";\n backBtn.className = \"sp-detail-back\";\n backBtn.setAttribute(\"aria-label\", this.t(\"detail.back\"));\n backBtn.appendChild(parseSvg(ICON_ARROW_LEFT));\n backBtn.addEventListener(\"click\", () => {\n this.hide();\n this.callbacks.onBack();\n });\n\n this.element.appendChild(header);\n header.appendChild(backBtn);\n\n // Title and badge are appended in show()\n\n // Scrollable content area\n this.content = el(\"div\", { class: \"sp-detail-content\" });\n this.element.appendChild(this.content);\n }\n\n /** Show the detail view for a specific feedback. */\n show(feedback: FeedbackResponse, number: number): void {\n this.currentFeedback = feedback;\n this.isProcessing = false;\n\n // ---- Update header ----\n const header = this.element.querySelector<HTMLElement>(\".sp-detail-header\");\n if (!header) return;\n // Remove old title/badge but keep the back button\n const backBtn = header.querySelector<HTMLElement>(\".sp-detail-back\");\n if (!backBtn) return;\n header.replaceChildren(backBtn);\n\n const title = el(\"span\", { class: \"sp-detail-title\" });\n setText(title, this.t(\"detail.title\").replace(\"{number}\", String(number)));\n header.appendChild(title);\n\n const badge = el(\"span\", { class: \"sp-badge\" });\n badge.style.background = getTypeBgColor(feedback.type, this.colors);\n badge.style.color = getTypeColor(feedback.type, this.colors);\n setText(badge, feedback.type);\n header.appendChild(badge);\n\n // ---- Build content sections ----\n this.content.replaceChildren();\n\n let sectionIndex = 0;\n\n // Section 1: Status + Actions\n const statusSection = this.buildSection(sectionIndex++);\n this.buildStatusActions(statusSection, feedback);\n this.content.appendChild(statusSection);\n\n // Section 2: Message\n const messageSection = this.buildSection(sectionIndex++);\n const messageSectionTitle = el(\"div\", { class: \"sp-detail-section-title\" });\n setText(messageSectionTitle, this.t(\"detail.message\"));\n messageSection.appendChild(messageSectionTitle);\n\n const messageBlock = el(\"div\", { class: \"sp-detail-message\" });\n messageBlock.style.borderLeftColor = getTypeColor(feedback.type, this.colors);\n setText(messageBlock, feedback.message);\n messageSection.appendChild(messageBlock);\n this.content.appendChild(messageSection);\n\n // Section 2b: Screenshot (when captured)\n if (feedback.screenshotUrl && isSafeImageUrl(feedback.screenshotUrl)) {\n const screenshotSection = this.buildSection(sectionIndex++);\n const screenshotSectionTitle = el(\"div\", { class: \"sp-detail-section-title\" });\n setText(screenshotSectionTitle, this.t(\"detail.screenshot\"));\n screenshotSection.appendChild(screenshotSectionTitle);\n\n const img = document.createElement(\"img\");\n img.className = \"sp-detail-screenshot\";\n img.src = feedback.screenshotUrl;\n img.alt = this.t(\"detail.screenshotAlt\");\n img.loading = \"lazy\";\n // Avoid leaking the panel viewer's referrer to the storage host —\n // a malicious or compromised storage URL could otherwise track which\n // operators view which feedbacks.\n img.referrerPolicy = \"no-referrer\";\n screenshotSection.appendChild(img);\n this.content.appendChild(screenshotSection);\n }\n\n // Section 3: Metadata\n const metaSection = this.buildSection(sectionIndex++);\n const metaSectionTitle = el(\"div\", { class: \"sp-detail-section-title\" });\n setText(metaSectionTitle, this.t(\"detail.metadata\"));\n metaSection.appendChild(metaSectionTitle);\n this.buildMetadata(metaSection, feedback);\n this.content.appendChild(metaSection);\n\n // Section 4: Annotation (if any)\n if (feedback.annotations.length > 0) {\n const annSection = this.buildSection(sectionIndex++);\n const annSectionTitle = el(\"div\", { class: \"sp-detail-section-title\" });\n annSectionTitle.appendChild(parseSvg(ICON_MAP_PIN));\n const annTitleText = el(\"span\");\n setText(annTitleText, this.t(\"detail.annotation\"));\n annSectionTitle.appendChild(annTitleText);\n annSection.appendChild(annSectionTitle);\n this.buildAnnotation(annSection, feedback);\n this.content.appendChild(annSection);\n }\n\n // Section 5: Diagnostics (only when present + non-empty).\n // Skipped silently when the widget was launched without capture, so legacy\n // records (and hosts who never opt in) never see an empty pane.\n if (hasDiagnostics(feedback.diagnostics)) {\n const diagSection = this.buildSection(sectionIndex++);\n const diagSectionTitle = el(\"div\", { class: \"sp-detail-section-title\" });\n diagSectionTitle.appendChild(parseSvg(ICON_TERMINAL));\n const diagTitleText = el(\"span\");\n setText(diagTitleText, this.t(\"detail.diagnostics\"));\n diagSectionTitle.appendChild(diagTitleText);\n diagSection.appendChild(diagSectionTitle);\n this.buildDiagnostics(diagSection, feedback);\n this.content.appendChild(diagSection);\n }\n\n // ---- Show with animation ----\n this._isVisible = true;\n this.element.setAttribute(\"aria-hidden\", \"false\");\n\n // Force reflow before adding visible class to trigger CSS transition\n void this.element.offsetHeight;\n this.element.classList.add(\"sp-detail--visible\");\n\n // Focus the back button for keyboard users\n requestAnimationFrame(() => {\n backBtn.focus();\n });\n }\n\n /** Hide the detail view with slide-out animation. */\n hide(): void {\n if (!this._isVisible) return;\n this._isVisible = false;\n this.element.classList.remove(\"sp-detail--visible\");\n this.element.setAttribute(\"aria-hidden\", \"true\");\n this.currentFeedback = null;\n this.resolveBtn = null;\n this.deleteBtn = null;\n }\n\n /** Whether the detail view is currently visible. */\n get isVisible(): boolean {\n return this._isVisible;\n }\n\n /** Cleanup all references. */\n destroy(): void {\n this.hide();\n this.element.remove();\n }\n\n // -----------------------------------------------------------------------\n // Private — Section builders\n // -----------------------------------------------------------------------\n\n /** Create a section element with stagger delay. */\n private buildSection(index: number): HTMLElement {\n const section = el(\"div\", { class: \"sp-detail-section\" });\n section.style.animationDelay = `${index * 40}ms`;\n return section;\n }\n\n /** Build Status pill + Resolve/Delete action buttons. */\n private buildStatusActions(container: HTMLElement, feedback: FeedbackResponse): void {\n const isResolved = feedback.status === \"resolved\";\n\n // Section title\n const sectionTitle = el(\"div\", { class: \"sp-detail-section-title\" });\n setText(sectionTitle, this.t(\"detail.status\"));\n container.appendChild(sectionTitle);\n\n // Status pill\n const statusRow = el(\"div\", { class: \"sp-detail-status\" });\n const pill = el(\"span\", {\n class: `sp-detail-status-pill ${isResolved ? \"sp-detail-status-pill--resolved\" : \"sp-detail-status-pill--open\"}`,\n });\n const dot = el(\"span\", { class: \"sp-detail-status-dot\" });\n dot.style.background = isResolved ? \"#9ca3af\" : \"#22c55e\";\n pill.appendChild(dot);\n const pillLabel = el(\"span\");\n setText(pillLabel, isResolved ? this.t(\"detail.reopen\") : this.t(\"detail.resolve\"));\n // Actually label the pill with Open/Resolved\n setText(pillLabel, isResolved ? \"Resolved\" : \"Open\");\n pill.appendChild(pillLabel);\n statusRow.appendChild(pill);\n container.appendChild(statusRow);\n\n // Action buttons\n const actions = el(\"div\", { class: \"sp-detail-actions\" });\n\n // Resolve / Reopen\n this.resolveBtn = document.createElement(\"button\");\n this.resolveBtn.type = \"button\";\n if (isResolved) {\n this.resolveBtn.className = \"sp-detail-btn-reopen\";\n this.resolveBtn.appendChild(parseSvg(ICON_UNDO));\n const span = document.createElement(\"span\");\n setText(span, this.t(\"detail.reopen\"));\n this.resolveBtn.appendChild(span);\n } else {\n this.resolveBtn.className = \"sp-detail-btn-resolve\";\n this.resolveBtn.appendChild(parseSvg(ICON_CHECK));\n const span = document.createElement(\"span\");\n setText(span, this.t(\"detail.resolve\"));\n this.resolveBtn.appendChild(span);\n }\n this.resolveBtn.addEventListener(\"click\", () => this.handleResolve());\n\n // Delete\n this.deleteBtn = document.createElement(\"button\");\n this.deleteBtn.type = \"button\";\n this.deleteBtn.className = \"sp-detail-btn-delete\";\n this.deleteBtn.appendChild(parseSvg(ICON_TRASH));\n const deleteSpan = document.createElement(\"span\");\n setText(deleteSpan, this.t(\"detail.delete\"));\n this.deleteBtn.appendChild(deleteSpan);\n this.deleteBtn.addEventListener(\"click\", () => this.handleDelete());\n\n actions.appendChild(this.resolveBtn);\n actions.appendChild(this.deleteBtn);\n container.appendChild(actions);\n }\n\n /** Build the metadata grid. */\n private buildMetadata(container: HTMLElement, feedback: FeedbackResponse): void {\n const meta = el(\"div\", { class: \"sp-detail-meta\" });\n\n // Page\n this.addMetaRow(meta, ICON_LINK, this.t(\"detail.page\"), () => {\n const value = el(\"div\", { class: \"sp-detail-meta-value\" });\n const pathname = extractPathname(feedback.url);\n setText(value, truncate(pathname, 60));\n value.title = feedback.url;\n return value;\n });\n\n // Author\n this.addMetaRow(meta, ICON_USER, this.t(\"detail.author\"), () => {\n const value = el(\"div\", { class: \"sp-detail-meta-value\" });\n const name = feedback.authorName || \"Anonymous\";\n const email = feedback.authorEmail;\n setText(value, email ? `${name} (${email})` : name);\n return value;\n });\n\n // Date\n this.addMetaRow(meta, ICON_CALENDAR, this.t(\"detail.date\"), () => {\n const value = el(\"div\", { class: \"sp-detail-meta-value\" });\n setText(value, formatFullDate(feedback.createdAt, this.locale.startsWith(\"fr\") ? \"fr\" : \"en\"));\n return value;\n });\n\n // Viewport\n this.addMetaRow(meta, ICON_MONITOR, this.t(\"detail.viewport\"), () => {\n const value = el(\"div\", { class: \"sp-detail-meta-value sp-detail-meta-value--mono\" });\n setText(value, feedback.viewport || \"Unknown\");\n return value;\n });\n\n // Browser\n this.addMetaRow(\n meta,\n `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"2\" y1=\"12\" x2=\"22\" y2=\"12\"/><path d=\"M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z\"/></svg>`,\n this.t(\"detail.browser\"),\n () => {\n const value = el(\"div\", { class: \"sp-detail-meta-value\" });\n setText(value, parseBrowser(feedback.userAgent));\n return value;\n },\n );\n\n // Resolved at (only if resolved)\n if (feedback.resolvedAt) {\n const resolvedDate = feedback.resolvedAt;\n this.addMetaRow(meta, ICON_CHECK, this.t(\"detail.resolvedAt\"), () => {\n const value = el(\"div\", { class: \"sp-detail-meta-value sp-detail-meta-value--secondary\" });\n setText(value, formatFullDate(resolvedDate, this.locale.startsWith(\"fr\") ? \"fr\" : \"en\"));\n return value;\n });\n }\n\n container.appendChild(meta);\n }\n\n /** Add a single metadata row with icon, label, and custom value element. */\n private addMetaRow(container: HTMLElement, iconSvg: string, label: string, buildValue: () => HTMLElement): void {\n const row = el(\"div\", { class: \"sp-detail-meta-row\" });\n row.appendChild(parseSvg(iconSvg));\n\n const content = el(\"div\", { class: \"sp-detail-meta-content\" });\n const labelEl = el(\"div\", { class: \"sp-detail-meta-label\" });\n setText(labelEl, label);\n content.appendChild(labelEl);\n content.appendChild(buildValue());\n\n row.appendChild(content);\n container.appendChild(row);\n }\n\n /** Build the annotation detail section. */\n private buildAnnotation(container: HTMLElement, feedback: FeedbackResponse): void {\n const ann = feedback.annotations[0];\n if (!ann) return;\n\n const wrapper = el(\"div\", { class: \"sp-detail-annotation\" });\n\n // Info card\n const info = el(\"div\", { class: \"sp-detail-annotation-info\" });\n\n // Element tag\n this.addAnnotationRow(info, ICON_CODE, this.t(\"detail.element\"), () => {\n const value = el(\"span\", { class: \"sp-detail-annotation-value sp-detail-annotation-value--mono\" });\n const tagDisplay = ann.elementId ? `<${ann.elementTag}#${ann.elementId}>` : `<${ann.elementTag}>`;\n setText(value, tagDisplay);\n return value;\n });\n\n // CSS selector\n this.addAnnotationRow(info, ICON_CROSSHAIR, this.t(\"detail.selector\"), () => {\n const value = el(\"span\", { class: \"sp-detail-annotation-value sp-detail-annotation-value--mono\" });\n setText(value, truncate(ann.cssSelector, 60));\n value.title = ann.cssSelector;\n return value;\n });\n\n // Position\n this.addAnnotationRow(info, ICON_MAP_PIN, this.t(\"detail.position\"), () => {\n const value = el(\"span\", { class: \"sp-detail-annotation-value\" });\n setText(\n value,\n `${ann.xPct.toFixed(1)}%, ${ann.yPct.toFixed(1)}%` +\n (ann.wPct > 0 || ann.hPct > 0 ? ` (${ann.wPct.toFixed(1)}% \\u00d7 ${ann.hPct.toFixed(1)}%)` : \"\"),\n );\n return value;\n });\n\n wrapper.appendChild(info);\n\n // \"Go to annotation\" button\n const gotoBtn = document.createElement(\"button\");\n gotoBtn.type = \"button\";\n gotoBtn.className = \"sp-detail-btn-goto\";\n gotoBtn.appendChild(parseSvg(ICON_MAP_PIN));\n const gotoLabel = document.createElement(\"span\");\n setText(gotoLabel, this.t(\"detail.goToAnnotation\"));\n gotoBtn.appendChild(gotoLabel);\n gotoBtn.addEventListener(\"click\", () => {\n if (this.currentFeedback) {\n this.callbacks.onGoToAnnotation(this.currentFeedback);\n }\n });\n\n wrapper.appendChild(gotoBtn);\n container.appendChild(wrapper);\n }\n\n /**\n * Build the collapsible Diagnostics section.\n *\n * Toggle expand/collapse via a single button; the body is hidden by default\n * to keep the detail view compact. Console entries get a colour-coded level\n * pill, network entries get a status/method/url row truncated to fit.\n */\n private buildDiagnostics(container: HTMLElement, feedback: FeedbackResponse): void {\n const diag = feedback.diagnostics;\n if (!diag) return;\n\n const consoleEntries = Array.isArray(diag.console) ? diag.console : [];\n const networkEntries = Array.isArray(diag.network) ? diag.network : [];\n const errorCount = consoleEntries.filter((e) => e.level === \"error\").length;\n\n const wrapper = el(\"div\", { class: \"sp-detail-diag\" });\n\n const toggle = document.createElement(\"button\");\n toggle.type = \"button\";\n toggle.className = \"sp-detail-diag-toggle\";\n toggle.setAttribute(\"aria-expanded\", \"false\");\n toggle.setAttribute(\"aria-label\", this.t(\"detail.diagnostics.expand\"));\n const toggleLabel = document.createElement(\"span\");\n const leftRow = document.createElement(\"span\");\n leftRow.style.display = \"inline-flex\";\n leftRow.style.alignItems = \"center\";\n leftRow.style.gap = \"8px\";\n leftRow.appendChild(parseSvg(ICON_CHEVRON));\n setText(toggleLabel, this.t(\"detail.diagnostics\"));\n leftRow.appendChild(toggleLabel);\n toggle.appendChild(leftRow);\n\n const counts = el(\"span\", { class: \"sp-detail-diag-counts\" });\n const consoleCount = el(\"span\", {\n class: `sp-detail-diag-count${errorCount > 0 ? \" sp-detail-diag-count--errors\" : \"\"}`,\n });\n setText(consoleCount, `${consoleEntries.length} console`);\n const networkCount = el(\"span\", {\n class: `sp-detail-diag-count${networkEntries.length > 0 ? \" sp-detail-diag-count--errors\" : \"\"}`,\n });\n setText(networkCount, `${networkEntries.length} net`);\n counts.appendChild(consoleCount);\n counts.appendChild(networkCount);\n toggle.appendChild(counts);\n\n const body = el(\"div\", { class: \"sp-detail-diag-body\" });\n\n // ---- Console group ----\n if (consoleEntries.length > 0) {\n const group = document.createElement(\"div\");\n const title = el(\"div\", { class: \"sp-detail-diag-group-title\" });\n setText(title, this.t(\"detail.diagnostics.console\"));\n group.appendChild(title);\n\n const list = document.createElement(\"ul\");\n list.className = \"sp-detail-diag-list\";\n for (const entry of consoleEntries) {\n const item = document.createElement(\"li\");\n const level = el(\"span\", {\n class: `sp-detail-diag-level sp-detail-diag-level--${entry.level}`,\n });\n setText(level, entry.level);\n const msg = el(\"span\", { class: \"sp-detail-diag-message\" });\n // Cap the rendered message length to keep the list readable.\n setText(msg, truncate(entry.message, 240));\n msg.title = entry.message;\n item.appendChild(level);\n item.appendChild(msg);\n list.appendChild(item);\n }\n group.appendChild(list);\n body.appendChild(group);\n }\n\n // ---- Network group ----\n if (networkEntries.length > 0) {\n const group = document.createElement(\"div\");\n const title = el(\"div\", { class: \"sp-detail-diag-group-title\" });\n setText(title, this.t(\"detail.diagnostics.network\"));\n group.appendChild(title);\n\n const list = document.createElement(\"ul\");\n list.className = \"sp-detail-diag-list\";\n for (const entry of networkEntries) {\n const item = document.createElement(\"li\");\n item.classList.add(\"sp-detail-diag-net\");\n const status = el(\"span\", { class: \"sp-detail-diag-net-status\" });\n setText(status, entry.status === 0 ? \"ERR\" : String(entry.status));\n const method = el(\"span\", { class: \"sp-detail-diag-net-method\" });\n setText(method, entry.method);\n const url = el(\"span\", { class: \"sp-detail-diag-net-url\" });\n setText(url, truncate(entry.url, 120));\n url.title = `${entry.url} — ${formatDuration(entry.durationMs)}`;\n item.appendChild(status);\n item.appendChild(method);\n item.appendChild(url);\n list.appendChild(item);\n }\n group.appendChild(list);\n body.appendChild(group);\n }\n\n toggle.addEventListener(\"click\", () => {\n const expanded = toggle.getAttribute(\"aria-expanded\") === \"true\";\n const next = !expanded;\n toggle.setAttribute(\"aria-expanded\", String(next));\n toggle.setAttribute(\n \"aria-label\",\n next ? this.t(\"detail.diagnostics.collapse\") : this.t(\"detail.diagnostics.expand\"),\n );\n body.classList.toggle(\"sp-detail-diag-body--open\", next);\n });\n\n wrapper.appendChild(toggle);\n wrapper.appendChild(body);\n container.appendChild(wrapper);\n }\n\n /** Add a single annotation info row. */\n private addAnnotationRow(\n container: HTMLElement,\n iconSvg: string,\n label: string,\n buildValue: () => HTMLElement,\n ): void {\n const row = el(\"div\", { class: \"sp-detail-annotation-row\" });\n row.appendChild(parseSvg(iconSvg));\n\n const content = el(\"div\", { class: \"sp-detail-meta-content\" });\n const labelEl = el(\"div\", { class: \"sp-detail-annotation-label\" });\n setText(labelEl, label);\n content.appendChild(labelEl);\n content.appendChild(buildValue());\n\n row.appendChild(content);\n container.appendChild(row);\n }\n\n // -----------------------------------------------------------------------\n // Private — Action handlers\n // -----------------------------------------------------------------------\n\n private async handleResolve(): Promise<void> {\n if (this.isProcessing || !this.currentFeedback) return;\n this.isProcessing = true;\n\n if (this.resolveBtn) this.setButtonLoading(this.resolveBtn);\n if (this.deleteBtn) this.deleteBtn.disabled = true;\n\n try {\n await this.callbacks.onResolve(this.currentFeedback);\n // The parent will call hide() or re-show with updated data\n } catch {\n // Restore buttons on error\n this.isProcessing = false;\n if (this.resolveBtn) this.restoreResolveBtn(this.currentFeedback);\n if (this.deleteBtn) this.deleteBtn.disabled = false;\n }\n }\n\n private async handleDelete(): Promise<void> {\n if (this.isProcessing || !this.currentFeedback) return;\n this.isProcessing = true;\n\n if (this.deleteBtn) this.setButtonLoading(this.deleteBtn);\n if (this.resolveBtn) this.resolveBtn.disabled = true;\n\n try {\n await this.callbacks.onDelete(this.currentFeedback);\n // The parent will call hide() after deletion\n } catch {\n this.isProcessing = false;\n if (this.deleteBtn) this.restoreDeleteBtn();\n if (this.resolveBtn) this.resolveBtn.disabled = false;\n }\n }\n\n private setButtonLoading(btn: HTMLButtonElement): void {\n btn.disabled = true;\n btn.replaceChildren(el(\"div\", { class: \"sp-spinner sp-spinner--sm\" }));\n }\n\n private restoreResolveBtn(feedback: FeedbackResponse): void {\n if (!this.resolveBtn) return;\n this.resolveBtn.disabled = false;\n this.resolveBtn.replaceChildren();\n\n const isResolved = feedback.status === \"resolved\";\n this.resolveBtn.appendChild(parseSvg(isResolved ? ICON_UNDO : ICON_CHECK));\n const span = document.createElement(\"span\");\n setText(span, isResolved ? this.t(\"detail.reopen\") : this.t(\"detail.resolve\"));\n this.resolveBtn.appendChild(span);\n }\n\n private restoreDeleteBtn(): void {\n if (!this.deleteBtn) return;\n this.deleteBtn.disabled = false;\n this.deleteBtn.replaceChildren();\n this.deleteBtn.appendChild(parseSvg(ICON_TRASH));\n const span = document.createElement(\"span\");\n setText(span, this.t(\"detail.delete\"));\n this.deleteBtn.appendChild(span);\n }\n}\n","/**\n * Sort and group-by-page controls for the feedback panel.\n *\n * Provides:\n * - Sort dropdown (newest, oldest, by-type, open-first)\n * - Group by page toggle (collapsible URL sections)\n * - Pure sort/group utility functions\n *\n * Glassmorphism design — glass surfaces, accent gradients,\n * smooth micro-interactions.\n */\n\nimport type { FeedbackResponse, FeedbackType } from \"@siteping/core\";\nimport { el, parseSvg, setText } from \"./dom-utils.js\";\nimport type { TFunction } from \"./i18n/index.js\";\nimport type { ThemeColors } from \"./styles/theme.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type SortMode = \"newest\" | \"oldest\" | \"by-type\" | \"open-first\";\n\n// ---------------------------------------------------------------------------\n// SVG Icons\n// ---------------------------------------------------------------------------\n\nexport const ICON_SORT = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M11 5h10\"/><path d=\"M11 9h7\"/><path d=\"M11 13h4\"/><path d=\"M3 17l3 3 3-3\"/><path d=\"M6 18V4\"/></svg>`;\n\nexport const ICON_PAGE = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"/><polyline points=\"14 2 14 8 20 8\"/><line x1=\"16\" y1=\"13\" x2=\"8\" y2=\"13\"/><line x1=\"16\" y1=\"17\" x2=\"8\" y2=\"17\"/><polyline points=\"10 9 9 9 8 9\"/></svg>`;\n\nexport const ICON_CHEVRON = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><polyline points=\"9 18 15 12 9 6\"/></svg>`;\n\n// ---------------------------------------------------------------------------\n// Sort utilities\n// ---------------------------------------------------------------------------\n\n/** Type priority for \"by-type\" sort: question, change, bug, other */\nconst TYPE_ORDER: Record<FeedbackType, number> = {\n question: 0,\n change: 1,\n bug: 2,\n other: 3,\n};\n\n/** Sort feedbacks according to the given mode. Returns a new array. */\nexport function sortFeedbacks(feedbacks: FeedbackResponse[], mode: SortMode): FeedbackResponse[] {\n const sorted = [...feedbacks];\n\n switch (mode) {\n case \"newest\":\n sorted.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());\n break;\n\n case \"oldest\":\n sorted.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());\n break;\n\n case \"by-type\":\n sorted.sort((a, b) => {\n const typeA = TYPE_ORDER[a.type] ?? 99;\n const typeB = TYPE_ORDER[b.type] ?? 99;\n if (typeA !== typeB) return typeA - typeB;\n // Within same type: newest first\n return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();\n });\n break;\n\n case \"open-first\":\n sorted.sort((a, b) => {\n // Open (0) before resolved (1)\n const statusA = a.status === \"open\" ? 0 : 1;\n const statusB = b.status === \"open\" ? 0 : 1;\n if (statusA !== statusB) return statusA - statusB;\n // Within same status: newest first\n return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();\n });\n break;\n }\n\n return sorted;\n}\n\n// ---------------------------------------------------------------------------\n// Group by page\n// ---------------------------------------------------------------------------\n\n/**\n * Extract the pathname from a full URL string.\n * Falls back to the raw string if URL parsing fails.\n */\nfunction extractPathname(url: string): string {\n try {\n return new URL(url).pathname;\n } catch {\n return url;\n }\n}\n\n/** Truncate a path string, keeping the beginning and end visible. */\nfunction truncatePath(path: string, maxLength: number): string {\n if (path.length <= maxLength) return path;\n const ellipsis = \"\\u2026\";\n const keep = Math.floor((maxLength - 1) / 2);\n return path.slice(0, keep) + ellipsis + path.slice(-keep);\n}\n\n/**\n * Group feedbacks by URL pathname.\n * Returns a Map sorted by feedback count descending (most feedbacks first).\n * Each group's feedbacks maintain their original order.\n */\nexport function groupFeedbacksByPage(feedbacks: FeedbackResponse[]): Map<string, FeedbackResponse[]> {\n const groups = new Map<string, FeedbackResponse[]>();\n\n for (const fb of feedbacks) {\n const path = extractPathname(fb.url);\n const existing = groups.get(path);\n if (existing) {\n existing.push(fb);\n } else {\n groups.set(path, [fb]);\n }\n }\n\n // Sort by count descending\n const sorted = new Map([...groups.entries()].sort((a, b) => b[1].length - a[1].length));\n\n return sorted;\n}\n\n// ---------------------------------------------------------------------------\n// Page group header\n// ---------------------------------------------------------------------------\n\n/**\n * Create a collapsible page group header element.\n * Click toggles the associated `.sp-group-content` sibling.\n */\nexport function createPageGroupHeader(pagePath: string, count: number, colors: ThemeColors): HTMLElement {\n const header = el(\"div\", { class: \"sp-group-header\" });\n header.setAttribute(\"role\", \"button\");\n header.setAttribute(\"tabindex\", \"0\");\n header.setAttribute(\"aria-expanded\", \"true\");\n header.style.borderBottomColor = colors.border;\n\n // Chevron\n const chevronWrap = el(\"span\", { class: \"sp-group-header-chevron\" });\n chevronWrap.appendChild(parseSvg(ICON_CHEVRON));\n header.appendChild(chevronWrap);\n\n // Page icon\n const pageIcon = el(\"span\", { class: \"sp-group-header-icon\" });\n pageIcon.appendChild(parseSvg(ICON_PAGE));\n header.appendChild(pageIcon);\n\n // Path\n const pathEl = el(\"span\", { class: \"sp-group-header-path\" });\n const displayPath = truncatePath(pagePath, 40);\n setText(pathEl, displayPath);\n if (pagePath.length > 40) {\n pathEl.title = pagePath;\n }\n header.appendChild(pathEl);\n\n // Count badge\n const countEl = el(\"span\", { class: \"sp-group-header-count\" });\n countEl.style.background = colors.accentLight;\n countEl.style.color = colors.accent;\n setText(countEl, String(count));\n header.appendChild(countEl);\n\n // Toggle behavior\n const toggle = () => {\n const isExpanded = header.getAttribute(\"aria-expanded\") === \"true\";\n header.setAttribute(\"aria-expanded\", String(!isExpanded));\n header.classList.toggle(\"sp-group-header--collapsed\", isExpanded);\n\n const content = header.nextElementSibling;\n if (content?.classList.contains(\"sp-group-content\")) {\n content.classList.toggle(\"sp-group-content--collapsed\", isExpanded);\n }\n };\n\n header.addEventListener(\"click\", toggle);\n header.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n toggle();\n }\n });\n\n return header;\n}\n\n// ---------------------------------------------------------------------------\n// PanelSortControls\n// ---------------------------------------------------------------------------\n\nexport class PanelSortControls {\n readonly element: HTMLElement;\n\n private _sortMode: SortMode = \"newest\";\n private _groupByPage = false;\n private menuEl: HTMLElement | null = null;\n private sortBtn: HTMLButtonElement;\n private groupToggle: HTMLButtonElement;\n private readonly t: TFunction;\n private readonly colors: ThemeColors;\n private readonly onChange: () => void;\n private outsideClickHandler: ((e: MouseEvent) => void) | null = null;\n\n constructor(colors: ThemeColors, onChange: () => void, t: TFunction) {\n this.colors = colors;\n this.onChange = onChange;\n this.t = t;\n\n this.element = el(\"div\", { class: \"sp-sort-controls\" });\n\n // Sort button\n this.sortBtn = document.createElement(\"button\");\n this.sortBtn.className = \"sp-sort-btn\";\n this.sortBtn.setAttribute(\"aria-haspopup\", \"listbox\");\n this.sortBtn.setAttribute(\"aria-expanded\", \"false\");\n this.sortBtn.setAttribute(\"aria-label\", this.t(\"sort.label\"));\n\n const sortIcon = parseSvg(ICON_SORT);\n this.sortBtn.appendChild(sortIcon);\n\n const sortLabel = el(\"span\", { class: \"sp-sort-btn-label\" });\n setText(sortLabel, this.t(\"sort.newest\"));\n this.sortBtn.appendChild(sortLabel);\n\n this.sortBtn.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n this.toggleMenu();\n });\n\n // Group by page toggle\n this.groupToggle = document.createElement(\"button\");\n this.groupToggle.className = \"sp-group-toggle\";\n this.groupToggle.setAttribute(\"aria-pressed\", \"false\");\n\n const groupIcon = parseSvg(ICON_PAGE);\n this.groupToggle.appendChild(groupIcon);\n\n const groupLabel = el(\"span\", { class: \"sp-group-toggle-label\" });\n setText(groupLabel, this.t(\"group.byPage\"));\n this.groupToggle.appendChild(groupLabel);\n\n this.groupToggle.addEventListener(\"click\", () => {\n this._groupByPage = !this._groupByPage;\n this.groupToggle.classList.toggle(\"sp-group-toggle--active\", this._groupByPage);\n this.groupToggle.setAttribute(\"aria-pressed\", String(this._groupByPage));\n this.onChange();\n });\n\n this.element.appendChild(this.sortBtn);\n this.element.appendChild(this.groupToggle);\n }\n\n get sortMode(): SortMode {\n return this._sortMode;\n }\n\n get groupByPage(): boolean {\n return this._groupByPage;\n }\n\n private toggleMenu(): void {\n if (this.menuEl) {\n this.closeMenu();\n return;\n }\n this.openMenu();\n }\n\n private openMenu(): void {\n this.menuEl = el(\"div\", { class: \"sp-sort-menu\" });\n this.menuEl.setAttribute(\"role\", \"listbox\");\n this.menuEl.setAttribute(\"aria-label\", this.t(\"sort.label\"));\n this.sortBtn.setAttribute(\"aria-expanded\", \"true\");\n\n const options: { mode: SortMode; label: string }[] = [\n { mode: \"newest\", label: this.t(\"sort.newest\") },\n { mode: \"oldest\", label: this.t(\"sort.oldest\") },\n { mode: \"by-type\", label: this.t(\"sort.byType\") },\n { mode: \"open-first\", label: this.t(\"sort.openFirst\") },\n ];\n\n for (const opt of options) {\n const item = document.createElement(\"button\");\n item.className = `sp-sort-option${opt.mode === this._sortMode ? \" sp-sort-option--active\" : \"\"}`;\n item.setAttribute(\"role\", \"option\");\n item.setAttribute(\"aria-selected\", String(opt.mode === this._sortMode));\n\n if (opt.mode === this._sortMode) {\n item.style.background = this.colors.accentLight;\n item.style.color = this.colors.accent;\n }\n\n setText(item, opt.label);\n\n item.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n this._sortMode = opt.mode;\n this.updateSortLabel();\n this.closeMenu();\n this.onChange();\n });\n\n this.menuEl.appendChild(item);\n }\n\n // Position relative to button\n this.element.appendChild(this.menuEl);\n\n // Close on outside click (next tick to avoid the current click)\n requestAnimationFrame(() => {\n this.outsideClickHandler = (e: MouseEvent) => {\n if (this.menuEl && !this.element.contains(e.target as Node)) {\n this.closeMenu();\n }\n };\n document.addEventListener(\"click\", this.outsideClickHandler, true);\n });\n\n // Close on Escape\n this.menuEl.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Escape\") {\n this.closeMenu();\n this.sortBtn.focus();\n }\n });\n }\n\n private closeMenu(): void {\n if (this.menuEl) {\n this.menuEl.remove();\n this.menuEl = null;\n }\n this.sortBtn.setAttribute(\"aria-expanded\", \"false\");\n if (this.outsideClickHandler) {\n document.removeEventListener(\"click\", this.outsideClickHandler, true);\n this.outsideClickHandler = null;\n }\n }\n\n private updateSortLabel(): void {\n const labelMap: Record<SortMode, string> = {\n newest: this.t(\"sort.newest\"),\n oldest: this.t(\"sort.oldest\"),\n \"by-type\": this.t(\"sort.byType\"),\n \"open-first\": this.t(\"sort.openFirst\"),\n };\n const label = this.sortBtn.querySelector(\".sp-sort-btn-label\");\n if (label) setText(label as HTMLElement, labelMap[this._sortMode]);\n }\n\n destroy(): void {\n this.closeMenu();\n }\n}\n\n// ---------------------------------------------------------------------------\n// CSS\n// ---------------------------------------------------------------------------\n\nexport const SORT_CSS = `\n /* ============================\n Sort Controls Container\n ============================ */\n\n .sp-sort-controls {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-top: 4px;\n padding-top: 8px;\n border-top: 1px solid var(--sp-border);\n }\n\n /* ============================\n Sort Dropdown Button\n ============================ */\n\n .sp-sort-btn {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n padding: 5px 12px;\n border-radius: var(--sp-radius-full);\n border: 1px solid var(--sp-border);\n background: var(--sp-glass-bg-heavy);\n color: var(--sp-text-secondary);\n font-family: var(--sp-font);\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n white-space: nowrap;\n transition: all 0.2s ease;\n position: relative;\n }\n\n .sp-sort-btn svg {\n width: 14px;\n height: 14px;\n flex-shrink: 0;\n }\n\n .sp-sort-btn:hover {\n border-color: var(--sp-accent);\n color: var(--sp-accent);\n background: var(--sp-accent-light);\n }\n\n .sp-sort-btn[aria-expanded=\"true\"] {\n border-color: var(--sp-accent);\n color: var(--sp-accent);\n background: var(--sp-accent-light);\n }\n\n /* ============================\n Sort Floating Menu\n ============================ */\n\n .sp-sort-menu {\n position: absolute;\n top: calc(100% + 6px);\n left: 0;\n min-width: 170px;\n padding: 4px;\n border-radius: var(--sp-radius);\n background: var(--sp-glass-bg-heavy);\n backdrop-filter: blur(var(--sp-blur-heavy));\n -webkit-backdrop-filter: blur(var(--sp-blur-heavy));\n border: 1px solid var(--sp-glass-border);\n box-shadow: var(--sp-shadow-md);\n z-index: 10;\n animation: sp-sort-menu-in 0.15s ease-out both;\n }\n\n @keyframes sp-sort-menu-in {\n from {\n opacity: 0;\n transform: translateY(-4px) scale(0.97);\n }\n to {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n }\n\n /* ============================\n Sort Menu Option\n ============================ */\n\n .sp-sort-option {\n display: block;\n width: 100%;\n padding: 8px 12px;\n border: none;\n border-radius: 8px;\n background: transparent;\n color: var(--sp-text-secondary);\n font-family: var(--sp-font);\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n text-align: left;\n transition: all 0.15s ease;\n }\n\n .sp-sort-option:hover {\n background: var(--sp-bg-hover);\n color: var(--sp-text);\n }\n\n .sp-sort-option--active {\n font-weight: 600;\n }\n\n .sp-sort-option--active:hover {\n background: var(--sp-accent-light);\n color: var(--sp-accent);\n }\n\n /* ============================\n Group by Page Toggle\n ============================ */\n\n .sp-group-toggle {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n padding: 5px 12px;\n border-radius: var(--sp-radius-full);\n border: 1px solid var(--sp-border);\n background: var(--sp-glass-bg-heavy);\n color: var(--sp-text-secondary);\n font-family: var(--sp-font);\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n white-space: nowrap;\n transition: all 0.2s ease;\n }\n\n .sp-group-toggle svg {\n width: 13px;\n height: 13px;\n flex-shrink: 0;\n }\n\n .sp-group-toggle:hover {\n border-color: var(--sp-accent);\n color: var(--sp-accent);\n background: var(--sp-accent-light);\n }\n\n .sp-group-toggle--active {\n background: var(--sp-accent-gradient);\n border-color: transparent;\n color: #fff;\n box-shadow: 0 2px 8px var(--sp-accent-glow);\n }\n\n .sp-group-toggle--active:hover {\n background: var(--sp-accent-gradient);\n border-color: transparent;\n color: #fff;\n }\n\n /* ============================\n Page Group Header\n ============================ */\n\n .sp-group-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n background: var(--sp-accent-light);\n border-bottom: 1px solid var(--sp-border);\n cursor: pointer;\n user-select: none;\n position: sticky;\n top: 0;\n z-index: 2;\n transition: background 0.2s ease;\n }\n\n .sp-group-header:hover {\n background: var(--sp-bg-hover);\n }\n\n .sp-group-header:focus-visible {\n outline: 2px solid var(--sp-accent);\n outline-offset: -2px;\n }\n\n .sp-group-header-chevron {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 16px;\n height: 16px;\n flex-shrink: 0;\n transition: transform 0.2s ease;\n transform: rotate(90deg);\n }\n\n .sp-group-header-chevron svg {\n width: 12px;\n height: 12px;\n color: var(--sp-text-tertiary);\n }\n\n .sp-group-header--collapsed .sp-group-header-chevron {\n transform: rotate(0deg);\n }\n\n .sp-group-header-icon {\n display: flex;\n align-items: center;\n flex-shrink: 0;\n }\n\n .sp-group-header-icon svg {\n width: 14px;\n height: 14px;\n color: var(--sp-text-tertiary);\n }\n\n .sp-group-header-path {\n font-size: 12px;\n font-weight: 600;\n color: var(--sp-text-secondary);\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .sp-group-header-count {\n font-size: 11px;\n font-weight: 700;\n padding: 1px 8px;\n border-radius: var(--sp-radius-full);\n flex-shrink: 0;\n font-variant-numeric: tabular-nums;\n }\n\n /* ============================\n Page Group Content\n ============================ */\n\n .sp-group-content {\n overflow: hidden;\n transition: max-height 0.25s ease, opacity 0.2s ease;\n max-height: 5000px;\n opacity: 1;\n }\n\n .sp-group-content--collapsed {\n max-height: 0;\n opacity: 0;\n pointer-events: none;\n }\n\n /* ============================\n Forced Colors / High Contrast\n ============================ */\n\n @media (forced-colors: active) {\n .sp-sort-btn,\n .sp-group-toggle,\n .sp-sort-option,\n .sp-group-header {\n border: 2px solid ButtonText !important;\n background: Canvas !important;\n color: ButtonText !important;\n }\n\n .sp-sort-btn:focus-visible,\n .sp-group-toggle:focus-visible,\n .sp-sort-option:focus-visible,\n .sp-group-header:focus-visible {\n outline: 3px solid Highlight !important;\n }\n\n .sp-sort-menu {\n border: 2px solid ButtonText !important;\n background: Canvas !important;\n }\n }\n\n /* ============================\n Reduced Motion\n ============================ */\n\n @media (prefers-reduced-motion: reduce) {\n .sp-sort-menu {\n animation: none;\n }\n .sp-group-header-chevron {\n transition: none;\n }\n .sp-group-content {\n transition: none;\n }\n }\n`;\n","import type { FeedbackResponse } from \"@siteping/core\";\nimport { el, setText } from \"./dom-utils.js\";\nimport type { TFunction } from \"./i18n/index.js\";\nimport type { ThemeColors } from \"./styles/theme.js\";\n\n// ---------------------------------------------------------------------------\n// CSS\n// ---------------------------------------------------------------------------\n\nexport const STATS_CSS = /* css */ `\n .sp-stats-bar {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 12px 24px;\n border-bottom: 1px solid var(--sp-border);\n user-select: none;\n }\n\n .sp-stats-bar[hidden] {\n display: none;\n }\n\n .sp-stats-row {\n display: flex;\n align-items: center;\n gap: 16px;\n }\n\n .sp-stats-item {\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n .sp-stats-dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n flex-shrink: 0;\n }\n\n .sp-stats-value {\n font-size: 16px;\n font-weight: 600;\n line-height: 1;\n color: var(--sp-text);\n font-variant-numeric: tabular-nums;\n font-feature-settings: \"tnum\";\n transition: opacity 0.3s ease;\n }\n\n .sp-stats-label {\n font-size: 11px;\n line-height: 1;\n color: var(--sp-text-tertiary);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n }\n\n .sp-stats-progress {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .sp-stats-progress-track {\n flex: 1;\n height: 4px;\n border-radius: 2px;\n background: var(--sp-border);\n overflow: hidden;\n }\n\n .sp-stats-progress-fill {\n height: 100%;\n border-radius: 2px;\n background: linear-gradient(90deg, var(--sp-accent), #22c55e);\n width: 0%;\n transition: width 0.5s ease;\n }\n\n .sp-stats-progress-label {\n font-size: 10px;\n line-height: 1;\n color: var(--sp-text-tertiary);\n white-space: nowrap;\n font-variant-numeric: tabular-nums;\n font-feature-settings: \"tnum\";\n min-width: 64px;\n text-align: right;\n }\n`;\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\n/**\n * Compact statistics bar displayed between filters and the feedback list.\n * Shows open/resolved/bug counts and a resolved-percentage progress bar.\n */\nexport class PanelStats {\n readonly element: HTMLElement;\n\n private readonly valueOpen: HTMLElement;\n private readonly valueResolved: HTMLElement;\n private readonly valueBugs: HTMLElement;\n private readonly progressFill: HTMLElement;\n private readonly progressLabel: HTMLElement;\n private readonly t: TFunction;\n\n constructor(\n private readonly colors: ThemeColors,\n t: TFunction,\n ) {\n this.t = t;\n // Container\n this.element = el(\"div\", { class: \"sp-stats-bar\" });\n this.element.setAttribute(\"aria-label\", \"Feedback statistics\");\n this.element.hidden = true;\n\n // --- Stats row ---\n const row = el(\"div\", { class: \"sp-stats-row\" });\n\n // Open\n const itemOpen = el(\"div\", { class: \"sp-stats-item\" });\n const dotOpen = el(\"span\", { class: \"sp-stats-dot\" });\n dotOpen.style.background = \"#22c55e\";\n this.valueOpen = el(\"span\", { class: \"sp-stats-value\" });\n setText(this.valueOpen, \"0\");\n const labelOpen = el(\"span\", { class: \"sp-stats-label\" });\n setText(labelOpen, this.t(\"stats.open\"));\n itemOpen.appendChild(dotOpen);\n itemOpen.appendChild(this.valueOpen);\n itemOpen.appendChild(labelOpen);\n\n // Resolved\n const itemResolved = el(\"div\", { class: \"sp-stats-item\" });\n const dotResolved = el(\"span\", { class: \"sp-stats-dot\" });\n dotResolved.style.background = \"#9ca3af\";\n this.valueResolved = el(\"span\", { class: \"sp-stats-value\" });\n setText(this.valueResolved, \"0\");\n const labelResolved = el(\"span\", { class: \"sp-stats-label\" });\n setText(labelResolved, this.t(\"stats.resolved\"));\n itemResolved.appendChild(dotResolved);\n itemResolved.appendChild(this.valueResolved);\n itemResolved.appendChild(labelResolved);\n\n // Bugs\n const itemBugs = el(\"div\", { class: \"sp-stats-item\" });\n const dotBugs = el(\"span\", { class: \"sp-stats-dot\" });\n dotBugs.style.background = this.colors.typeBug;\n this.valueBugs = el(\"span\", { class: \"sp-stats-value\" });\n setText(this.valueBugs, \"0\");\n const labelBugs = el(\"span\", { class: \"sp-stats-label\" });\n setText(labelBugs, this.t(\"stats.bugs\"));\n itemBugs.appendChild(dotBugs);\n itemBugs.appendChild(this.valueBugs);\n itemBugs.appendChild(labelBugs);\n\n row.appendChild(itemOpen);\n row.appendChild(itemResolved);\n row.appendChild(itemBugs);\n\n // --- Progress bar ---\n const progress = el(\"div\", { class: \"sp-stats-progress\" });\n const track = el(\"div\", { class: \"sp-stats-progress-track\" });\n this.progressFill = el(\"div\", { class: \"sp-stats-progress-fill\" });\n track.appendChild(this.progressFill);\n this.progressLabel = el(\"span\", { class: \"sp-stats-progress-label\" });\n setText(this.progressLabel, \"\");\n progress.appendChild(track);\n progress.appendChild(this.progressLabel);\n\n this.element.appendChild(row);\n this.element.appendChild(progress);\n }\n\n /** Update stats from current feedbacks array + total count. */\n update(feedbacks: FeedbackResponse[], total: number): void {\n // Hide when there are no feedbacks at all\n if (total === 0) {\n this.element.hidden = true;\n return;\n }\n this.element.hidden = false;\n\n let openCount = 0;\n let resolvedCount = 0;\n let bugCount = 0;\n\n for (const fb of feedbacks) {\n if (fb.status === \"open\") openCount++;\n if (fb.status === \"resolved\") resolvedCount++;\n if (fb.type === \"bug\") bugCount++;\n }\n\n setText(this.valueOpen, String(openCount));\n setText(this.valueResolved, String(resolvedCount));\n setText(this.valueBugs, String(bugCount));\n\n // Percentage resolved (relative to visible feedbacks, not total)\n const visible = feedbacks.length;\n const pct = visible > 0 ? Math.round((resolvedCount / visible) * 100) : 0;\n\n // Animate fill width via rAF so the transition kicks in after the DOM update\n requestAnimationFrame(() => {\n this.progressFill.style.width = `${pct}%`;\n });\n\n const progressText = this.t(\"stats.progress\").replace(\"{percent}\", String(pct));\n setText(this.progressLabel, progressText);\n }\n}\n","/**\n * Keyboard shortcuts system for the feedback panel.\n *\n * Gmail/GitHub-style single-key shortcuts:\n * J/K — navigate cards, R — resolve, D — delete,\n * F or / — focus search, X — toggle select, ? — help overlay.\n *\n * Active only when the panel is open. Ignores keypresses inside\n * input/textarea elements. Uses a Map<string, handler> for O(1) lookup.\n */\n\nimport { el, parseSvg, setText } from \"./dom-utils.js\";\nimport type { TFunction, Translations } from \"./i18n/index.js\";\nimport type { ThemeColors } from \"./styles/theme.js\";\n\n// ---------------------------------------------------------------------------\n// Icon\n// ---------------------------------------------------------------------------\n\nexport const ICON_KEYBOARD = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><rect x=\"2\" y=\"4\" width=\"20\" height=\"16\" rx=\"2\"/><path d=\"M6 8h.01\"/><path d=\"M10 8h.01\"/><path d=\"M14 8h.01\"/><path d=\"M18 8h.01\"/><path d=\"M6 12h.01\"/><path d=\"M18 12h.01\"/><path d=\"M8 16h8\"/></svg>`;\n\n// All translation keys are sourced from the central i18n dict via t().\ntype ShortcutsI18nKey = Extract<keyof Translations, `shortcuts.${string}`>;\n\n// ---------------------------------------------------------------------------\n// Callbacks\n// ---------------------------------------------------------------------------\n\nexport interface ShortcutCallbacks {\n onNavigate: (direction: \"up\" | \"down\") => void;\n onResolve: () => void;\n onDelete: () => void;\n onFocusSearch: () => void;\n onToggleSelect: () => void;\n}\n\n// ---------------------------------------------------------------------------\n// Card focus helpers\n// ---------------------------------------------------------------------------\n\n/** Get the currently focused card index from the list container (-1 if none). */\nexport function getFocusedCardIndex(listContainer: HTMLElement): number {\n const cards = listContainer.querySelectorAll<HTMLElement>(\".sp-card\");\n for (let i = 0; i < cards.length; i++) {\n if (cards[i]?.classList.contains(\"sp-card--focused\")) return i;\n }\n return -1;\n}\n\n/** Focus a card by index in the list container. Clamps to valid range. */\nexport function focusCardByIndex(listContainer: HTMLElement, index: number): void {\n const cards = listContainer.querySelectorAll<HTMLElement>(\".sp-card\");\n if (cards.length === 0) return;\n\n // Remove previous focus\n for (const card of cards) {\n card.classList.remove(\"sp-card--focused\");\n }\n\n const clamped = Math.max(0, Math.min(index, cards.length - 1));\n const target = cards[clamped];\n if (!target) return;\n target.classList.add(\"sp-card--focused\");\n target.scrollIntoView({ block: \"nearest\", behavior: \"smooth\" });\n target.focus({ preventScroll: true });\n}\n\n// ---------------------------------------------------------------------------\n// Shortcut definitions (used to build help grid)\n// ---------------------------------------------------------------------------\n\ninterface ShortcutDef {\n keys: string[];\n label: ShortcutsI18nKey;\n}\n\nconst SHORTCUT_DEFS: ShortcutDef[] = [\n { keys: [\"J\", \"K\"], label: \"shortcuts.navigate\" },\n { keys: [\"R\"], label: \"shortcuts.resolve\" },\n { keys: [\"D\"], label: \"shortcuts.delete\" },\n { keys: [\"F\", \"/\"], label: \"shortcuts.search\" },\n { keys: [\"X\"], label: \"shortcuts.select\" },\n { keys: [\"?\"], label: \"shortcuts.help\" },\n { keys: [\"Esc\"], label: \"shortcuts.close\" },\n];\n\n// ---------------------------------------------------------------------------\n// CSS\n// ---------------------------------------------------------------------------\n\nexport const SHORTCUTS_CSS = /* css */ `\n /* ---- Help overlay backdrop ---- */\n\n .sp-shortcuts-overlay {\n position: fixed;\n inset: 0;\n background: var(--sp-backdrop, rgba(15, 23, 42, 0.2));\n backdrop-filter: blur(var(--sp-blur));\n -webkit-backdrop-filter: blur(var(--sp-blur));\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10;\n opacity: 0;\n pointer-events: none;\n transition: opacity 0.2s ease;\n }\n\n .sp-shortcuts-overlay--visible {\n opacity: 1;\n pointer-events: auto;\n }\n\n /* ---- Glassmorphism card ---- */\n\n .sp-shortcuts-card {\n width: 380px;\n max-width: calc(100vw - 32px);\n padding: 24px 28px 20px;\n border-radius: 20px;\n background: var(--sp-glass-bg-heavy);\n backdrop-filter: blur(var(--sp-blur-heavy));\n -webkit-backdrop-filter: blur(var(--sp-blur-heavy));\n border: 1px solid var(--sp-glass-border);\n box-shadow: var(--sp-shadow-xl);\n font-family: var(--sp-font);\n position: relative;\n transform: scale(0.92) translateY(8px);\n transition: transform 0.25s cubic-bezier(0.16, 1, 0.3, 1);\n }\n\n .sp-shortcuts-overlay--visible .sp-shortcuts-card {\n transform: scale(1) translateY(0);\n }\n\n /* ---- Title row ---- */\n\n .sp-shortcuts-title {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 16px;\n font-weight: 700;\n color: var(--sp-text);\n margin-bottom: 18px;\n }\n\n .sp-shortcuts-title svg {\n width: 18px;\n height: 18px;\n color: var(--sp-text-secondary);\n flex-shrink: 0;\n }\n\n /* ---- Close button ---- */\n\n .sp-shortcuts-close {\n position: absolute;\n top: 16px;\n right: 16px;\n width: 28px;\n height: 28px;\n border-radius: 8px;\n border: none;\n background: transparent;\n color: var(--sp-text-tertiary);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background 0.15s ease, color 0.15s ease;\n }\n\n .sp-shortcuts-close:hover {\n background: var(--sp-bg-hover);\n color: var(--sp-text);\n }\n\n .sp-shortcuts-close svg {\n width: 14px;\n height: 14px;\n }\n\n /* ---- Two-column grid ---- */\n\n .sp-shortcuts-grid {\n display: flex;\n flex-direction: column;\n gap: 10px;\n }\n\n .sp-shortcuts-row {\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n .sp-shortcuts-keys {\n display: flex;\n align-items: center;\n gap: 4px;\n min-width: 80px;\n justify-content: flex-end;\n }\n\n .sp-shortcuts-separator {\n font-size: 11px;\n color: var(--sp-text-tertiary);\n user-select: none;\n }\n\n /* ---- Key badge (<kbd> styling) ---- */\n\n .sp-kbd {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 28px;\n height: 26px;\n padding: 0 7px;\n border-radius: 6px;\n background: var(--sp-bg-hover);\n border: 1px solid var(--sp-border);\n box-shadow:\n inset 0 -1px 0 rgba(0, 0, 0, 0.08),\n 0 1px 2px rgba(0, 0, 0, 0.04);\n font-family: ui-monospace, \"SF Mono\", \"Cascadia Code\", \"Segoe UI Mono\", Menlo, monospace;\n font-size: 12px;\n font-weight: 600;\n color: var(--sp-text);\n text-align: center;\n line-height: 1;\n user-select: none;\n }\n\n /* ---- Description text ---- */\n\n .sp-shortcuts-desc {\n font-size: 13px;\n color: var(--sp-text-secondary);\n line-height: 1.3;\n }\n\n /* ---- Hint button (bottom-right of panel) ---- */\n\n .sp-shortcuts-hint {\n width: 24px;\n height: 24px;\n border-radius: var(--sp-radius-full);\n border: 1px solid var(--sp-border);\n background: var(--sp-bg-hover);\n color: var(--sp-text-tertiary);\n font-family: ui-monospace, \"SF Mono\", \"Cascadia Code\", \"Segoe UI Mono\", Menlo, monospace;\n font-size: 12px;\n font-weight: 700;\n line-height: 1;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;\n position: absolute;\n bottom: 12px;\n right: 12px;\n }\n\n .sp-shortcuts-hint:hover {\n background: var(--sp-accent-light);\n color: var(--sp-accent);\n border-color: var(--sp-accent);\n }\n\n .sp-shortcuts-hint::after {\n content: attr(aria-label);\n position: absolute;\n bottom: calc(100% + 6px);\n right: 0;\n padding: 4px 8px;\n border-radius: 6px;\n background: var(--sp-glass-bg-heavy);\n border: 1px solid var(--sp-glass-border);\n box-shadow: var(--sp-shadow-sm);\n font-family: var(--sp-font);\n font-size: 11px;\n font-weight: 500;\n color: var(--sp-text-secondary);\n white-space: nowrap;\n opacity: 0;\n pointer-events: none;\n transform: translateY(4px);\n transition: opacity 0.15s ease, transform 0.15s ease;\n }\n\n .sp-shortcuts-hint:hover::after {\n opacity: 1;\n transform: translateY(0);\n }\n\n /* ---- Card focus highlight (navigation) ---- */\n\n .sp-card--focused {\n outline: 2px solid var(--sp-accent);\n outline-offset: -2px;\n border-radius: inherit;\n }\n\n /* ---- Reduced motion ---- */\n\n @media (prefers-reduced-motion: reduce) {\n .sp-shortcuts-overlay,\n .sp-shortcuts-card,\n .sp-shortcuts-close,\n .sp-shortcuts-hint,\n .sp-shortcuts-hint::after {\n transition-duration: 0.01ms !important;\n }\n }\n`;\n\n// ---------------------------------------------------------------------------\n// Close icon (small X) — reused from icons.ts pattern\n// ---------------------------------------------------------------------------\n\nconst ICON_CLOSE_SM = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/></svg>`;\n\n// ---------------------------------------------------------------------------\n// KeyboardShortcuts\n// ---------------------------------------------------------------------------\n\nexport class KeyboardShortcuts {\n /** Help overlay element — append to shadow root. */\n readonly helpOverlay: HTMLElement;\n /** Small \"?\" hint button — append inside the panel. */\n readonly hintButton: HTMLButtonElement;\n\n private readonly keyMap: Map<string, () => void>;\n private readonly boundHandler: (e: KeyboardEvent) => void;\n private shadowRoot: ShadowRoot | HTMLElement | null = null;\n private enabled = false;\n private helpVisible = false;\n private destroyed = false;\n\n constructor(\n _colors: ThemeColors,\n callbacks: ShortcutCallbacks,\n private readonly t: TFunction,\n ) {\n // Build key map (O(1) dispatch)\n this.keyMap = new Map<string, () => void>([\n [\"j\", () => callbacks.onNavigate(\"down\")],\n [\"k\", () => callbacks.onNavigate(\"up\")],\n [\"r\", () => callbacks.onResolve()],\n [\"d\", () => callbacks.onDelete()],\n [\"f\", () => callbacks.onFocusSearch()],\n [\"/\", () => callbacks.onFocusSearch()],\n [\"x\", () => callbacks.onToggleSelect()],\n [\"?\", () => this.toggleHelp()],\n ]);\n\n // Build DOM\n this.helpOverlay = this.buildOverlay();\n this.hintButton = this.buildHintButton();\n\n // Bind handler once\n this.boundHandler = (e: KeyboardEvent) => this.handleKeydown(e);\n }\n\n // -------------------------------------------------------------------------\n // Public API\n // -------------------------------------------------------------------------\n\n /** Enable shortcuts. Provide the shadow root (or document) to attach the listener. */\n enable(root?: ShadowRoot | HTMLElement): void {\n if (this.destroyed || this.enabled) return;\n if (root) this.shadowRoot = root;\n const target = this.shadowRoot ?? document;\n target.addEventListener(\"keydown\", this.boundHandler as EventListener);\n this.enabled = true;\n }\n\n /** Disable shortcuts (call when panel closes). */\n disable(): void {\n if (!this.enabled) return;\n const target = this.shadowRoot ?? document;\n target.removeEventListener(\"keydown\", this.boundHandler as EventListener);\n this.enabled = false;\n // Also hide help if visible\n if (this.helpVisible) this.hideHelp();\n }\n\n /** Show/hide help overlay. */\n toggleHelp(): void {\n if (this.helpVisible) {\n this.hideHelp();\n } else {\n this.showHelp();\n }\n }\n\n /** Destroy and clean up all listeners. */\n destroy(): void {\n if (this.destroyed) return;\n this.disable();\n this.helpOverlay.remove();\n this.hintButton.remove();\n this.destroyed = true;\n }\n\n // -------------------------------------------------------------------------\n // Keyboard handler\n // -------------------------------------------------------------------------\n\n private handleKeydown(e: KeyboardEvent): void {\n // Escape closes help overlay only (panel close handled elsewhere)\n if (e.key === \"Escape\") {\n if (this.helpVisible) {\n e.preventDefault();\n e.stopPropagation();\n this.hideHelp();\n }\n return;\n }\n\n // If help overlay is open, block all other shortcuts\n if (this.helpVisible) return;\n\n // Ignore when focus is in an input, textarea, or contenteditable\n const active = e.composedPath()[0] as HTMLElement | undefined;\n if (active) {\n const tag = active.tagName?.toLowerCase();\n if (tag === \"input\" || tag === \"textarea\" || tag === \"select\") return;\n if (active.isContentEditable) return;\n }\n\n // Ignore modified keys (Ctrl, Alt, Meta) — except Shift for \"?\"\n if (e.ctrlKey || e.altKey || e.metaKey) return;\n\n const handler = this.keyMap.get(e.key);\n if (handler) {\n e.preventDefault();\n e.stopPropagation();\n handler();\n }\n }\n\n // -------------------------------------------------------------------------\n // Help overlay visibility\n // -------------------------------------------------------------------------\n\n private showHelp(): void {\n this.helpVisible = true;\n this.helpOverlay.classList.add(\"sp-shortcuts-overlay--visible\");\n\n // Focus the close button for accessibility\n const closeBtn = this.helpOverlay.querySelector<HTMLButtonElement>(\".sp-shortcuts-close\");\n closeBtn?.focus();\n }\n\n private hideHelp(): void {\n this.helpVisible = false;\n this.helpOverlay.classList.remove(\"sp-shortcuts-overlay--visible\");\n }\n\n // -------------------------------------------------------------------------\n // DOM builders\n // -------------------------------------------------------------------------\n\n private buildOverlay(): HTMLElement {\n const overlay = el(\"div\", { class: \"sp-shortcuts-overlay\" });\n overlay.setAttribute(\"role\", \"dialog\");\n overlay.setAttribute(\"aria-modal\", \"true\");\n overlay.setAttribute(\"aria-label\", this.t(\"shortcuts.title\"));\n\n // Click backdrop to close\n overlay.addEventListener(\"click\", (e) => {\n if (e.target === overlay) this.hideHelp();\n });\n\n const card = el(\"div\", { class: \"sp-shortcuts-card\" });\n\n // Title\n const title = el(\"div\", { class: \"sp-shortcuts-title\" });\n title.appendChild(parseSvg(ICON_KEYBOARD));\n const titleText = el(\"span\");\n setText(titleText, this.t(\"shortcuts.title\"));\n title.appendChild(titleText);\n card.appendChild(title);\n\n // Close button\n const closeBtn = document.createElement(\"button\");\n closeBtn.className = \"sp-shortcuts-close\";\n closeBtn.setAttribute(\"aria-label\", this.t(\"shortcuts.close\"));\n closeBtn.appendChild(parseSvg(ICON_CLOSE_SM));\n closeBtn.addEventListener(\"click\", () => this.hideHelp());\n card.appendChild(closeBtn);\n\n // Grid\n const grid = el(\"div\", { class: \"sp-shortcuts-grid\" });\n\n for (const def of SHORTCUT_DEFS) {\n const row = el(\"div\", { class: \"sp-shortcuts-row\" });\n\n const keysWrap = el(\"div\", { class: \"sp-shortcuts-keys\" });\n def.keys.forEach((key, i) => {\n if (i > 0) {\n const sep = el(\"span\", { class: \"sp-shortcuts-separator\" });\n setText(sep, \"/\");\n keysWrap.appendChild(sep);\n }\n const kbd = el(\"span\", { class: \"sp-kbd\" });\n setText(kbd, key);\n keysWrap.appendChild(kbd);\n });\n\n const desc = el(\"span\", { class: \"sp-shortcuts-desc\" });\n setText(desc, this.t(def.label));\n\n row.appendChild(keysWrap);\n row.appendChild(desc);\n grid.appendChild(row);\n }\n\n card.appendChild(grid);\n overlay.appendChild(card);\n\n return overlay;\n }\n\n private buildHintButton(): HTMLButtonElement {\n const btn = document.createElement(\"button\");\n btn.className = \"sp-shortcuts-hint\";\n btn.setAttribute(\"aria-label\", this.t(\"shortcuts.hint\"));\n setText(btn, \"?\");\n btn.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n this.toggleHelp();\n });\n return btn;\n }\n}\n"]}