@metricinsights/pp-dev 1.4.7-beta.1 → 1.5.0-beta.1

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.
Files changed (53) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +67 -2
  3. package/dist/CHANGELOG.md +6 -0
  4. package/dist/README.md +67 -2
  5. package/dist/cjs/cli.js +1 -1
  6. package/dist/cjs/cli.js.map +1 -1
  7. package/dist/cjs/index-FgUi7Fqw.js +2 -0
  8. package/dist/cjs/index-FgUi7Fqw.js.map +1 -0
  9. package/dist/cjs/index.js +1 -1
  10. package/dist/cjs/migrate-pmX6NEmP.js +2 -0
  11. package/dist/cjs/{migrate-G-xh-wpc.js.map → migrate-pmX6NEmP.js.map} +1 -1
  12. package/dist/cjs/package.json +1 -1
  13. package/dist/cjs/plugin-yWlSj5sK.js +2 -0
  14. package/dist/cjs/plugin-yWlSj5sK.js.map +1 -0
  15. package/dist/cjs/plugin.js +1 -1
  16. package/dist/cjs/version-plugin-0EjcUgtP.js +2 -0
  17. package/dist/cjs/version-plugin-0EjcUgtP.js.map +1 -0
  18. package/dist/client/client.css +1 -1
  19. package/dist/client/client.css.map +1 -1
  20. package/dist/client/client.js +355 -0
  21. package/dist/client/client.js.map +1 -1
  22. package/dist/client/index.html +24 -2
  23. package/dist/esm/cli.js +1 -1
  24. package/dist/esm/cli.js.map +1 -1
  25. package/dist/esm/index-BA9vecJD.js +2 -0
  26. package/dist/esm/index-BA9vecJD.js.map +1 -0
  27. package/dist/esm/index.js +1 -1
  28. package/dist/esm/migrate-Dsd7Aqx-.js +2 -0
  29. package/dist/esm/{migrate-BBCvcE_8.js.map → migrate-Dsd7Aqx-.js.map} +1 -1
  30. package/dist/esm/package.json +1 -1
  31. package/dist/esm/plugin-CFtRWPwU.js +2 -0
  32. package/dist/esm/plugin-CFtRWPwU.js.map +1 -0
  33. package/dist/esm/plugin.js +1 -1
  34. package/dist/esm/version-plugin-pYbaqnAH.js +2 -0
  35. package/dist/esm/{version-plugin-BqEwLs0b.js.map → version-plugin-pYbaqnAH.js.map} +1 -1
  36. package/dist/types/index.d.ts +11 -0
  37. package/docs/features.md +20 -1
  38. package/docs/screenshots/13-users-popover.png +0 -0
  39. package/docs/screenshots/14-users-user-tab.png +0 -0
  40. package/package.json +2 -2
  41. package/dist/cjs/index-DlBoHBGI.js +0 -2
  42. package/dist/cjs/index-DlBoHBGI.js.map +0 -1
  43. package/dist/cjs/migrate-G-xh-wpc.js +0 -2
  44. package/dist/cjs/plugin-Bfd0zHTR.js +0 -2
  45. package/dist/cjs/plugin-Bfd0zHTR.js.map +0 -1
  46. package/dist/cjs/version-plugin-DmJ0luHT.js +0 -2
  47. package/dist/cjs/version-plugin-DmJ0luHT.js.map +0 -1
  48. package/dist/esm/index-Dv-Y3cH6.js +0 -2
  49. package/dist/esm/index-Dv-Y3cH6.js.map +0 -1
  50. package/dist/esm/migrate-BBCvcE_8.js +0 -2
  51. package/dist/esm/plugin-B0dsWaBT.js +0 -2
  52. package/dist/esm/plugin-B0dsWaBT.js.map +0 -1
  53. package/dist/esm/version-plugin-BqEwLs0b.js +0 -2
@@ -1 +1 @@
1
- {"version":3,"sourceRoot":"","sources":["client.scss"],"names":[],"mappings":"AA+FA,uBAtBI,sXA2BF,qDA3BE,sXA+BF,2CACE,eACA,SACA,WACA,YACA,6BACA,YACA,gDACA,uCACA,kBACA,aACA,sBACA,qBACA,QACA,aACA,gDACA,gBACA,eACA,uDACA,wBACA,UACA,sBACA,6DAEA,oDACE,wCACA,UAGF,mDACE,wCACA,UAGF,iDACE,aACA,uBACA,8BACA,QACA,UACA,aACA,WACA,sBACA,gBACA,cAEA,sDACE,aACA,mBACA,uBACA,WACA,YACA,cACA,sCAGF,sDACE,OACA,cACA,eACA,gBACA,iBACA,gBACA,sBAGF,uDACE,eACA,eACA,gBACA,aACA,mBACA,uBACA,UACA,SACA,gBACA,aACA,yBACA,kBACA,0BACA,cAEA,6DACE,oCAGF,qEACE,kDACA,mBAGF,2DACE,8BAKN,mDACE,iBACA,UACA,wBACA,gBACA,sBACA,YACA,+BACA,wBACA,eACA,gBACA,iBACA,gBACA,sBACA,qBAGF,oDACE,6DACA,mDAGF,mDACE,4DACA,kDAGF,iDACE,0DACA,gDAGF,oDACE,6DACA,mDAIF,iDACE,WACA,UACA,iBACA,uDAEA,mHAEE,yCAKN,yGAEE,eACA,QACA,eACA,aACA,mBACA,uBACA,iCACA,0BACA,kCAGF,6CACE,8BACA,kBACA,iCACA,gDACA,8CACA,gBAEA,mDACE,kBACA,eACA,gBACA,wCACA,oCACA,iCAGF,qDACE,aACA,qBACA,oCACA,eACA,gBAGF,qDACE,aACA,sBACA,QACA,oBAGF,iDACE,WACA,iBACA,kBACA,+BACA,eACA,gBACA,eACA,wBAEA,yDACE,oCACA,+BACA,4BAGF,0DACE,qCACA,kDAGF,uDACE,WAKN,uEAEE,eACA,eACA,mBACA,kBACA,OAxUY,KAyUZ,kBACA,YACA,QACA,gDACA,oCACA,sBACA,aACA,mBACA,gDACA,iBACA,0BACA,kCAMA,2EACE,oCACA,+BACA,eACA,gBACA,iBACA,iBACA,qBACA,uBACA,uBACA,sBACA,SACA,UACA,SACA,UACA,eACA,gBACA,mCACA,kCAGF,yFACE,aACA,mBACA,QAGF,yFACE,aACA,mBACA,kBACA,cAEA,yIACE,WACA,kBACA,QACA,QACA,2BACA,YACA,UACA,oCAEA,mMACE,sCAGF,+CAdF,yIAeI,sCAEA,qMACE,qCAKN,iHACE,eAGF,+GACE,gBAIJ,mFACE,aACA,mBACA,QACA,eACA,gBACA,iBAGF,qFACE,yCACA,gBAGF,qFACE,gBAGF,mFACE,uCACA,qBACA,0BACA,aACA,mBACA,QAEA,+FACE,uCACA,0BAQJ,yFACE,kDAGF,yFACE,kDAGF,mFACE,+CAGF,yFACE,kDAGF,uFACE,iDAGF,2EACE,uCACA,qBACA,sBACA,iBAEA,uFACE,qBAGF,gLAEE,UAIJ,qFACE,YACA,YACA,gBACA,eACA,aAGF,oLA/CE,gBAoDF,iGACE,aACA,mBACA,uBACA,cACA,WACA,YACA,YACA,kBACA,kBACA,yCACA,YACA,WACE,2CAGF,yGACE,oBACA,kBAGF,6GACE,UACA,oCAIJ,mGACE,aACA,mBACA,uBACA,WACA,YACA,YACA,kBACA,eACA,yCACA,wBAEA,2GACE,WACA,YACA,UACA,8BAGF,+GACE,uCACA,oCAEA,uHACE,wBAKN,2FACE,kBACA,gBACA,aACA,aACA,sBACA,SACA,gDACA,oCACA,kBACA,wCACA,eACA,eACA,mBACA,gBAEA,qJACE,eACA,gBACA,yCACA,yBACA,oBAGF,iJACE,aACA,mBACA,8BACA,SAGF,uJACE,gBACA,4BACA,WACA,YACA,8CACA,eACA,cAGF,+IACE,aACA,sCACA,QAGF,6IACE,WACA,YACA,oCACA,kBACA,eACA,kBACA,yBACA,iCAEA,2JACE,WACA,kBACA,WACA,WACA,kBACA,oDACA,qCAGF,+KACE,QACA,SAGF,iLACE,QACA,UAGF,qLACE,WACA,SAGF,uLACE,WACA,UAGF,yJACE,8CAGF,2JACE,8CAEA,yKACE,kDAKN,6IACE,aACA,QAGF,2IACE,gBACA,oCACA,kBACA,eACA,yBACA,eACA,iBACA,yCACA,WACE,+DAIF,uJACE,8CACA,uCAGF,yJACE,8CACA,kDACA,qCAGF,uKACE,mDACA,mBAIJ,2JACE,WACA,iBACA,mCACA,kBACA,eACA,gBACA,sCACA,qCAEA,uKACE,qCAIJ,qJACE,aACA,sBACA,QACA,iBACA,wCAGF,iJACE,aACA,sBACA,QAGF,yIACE,WACA,iBACA,kBACA,eACA,gBACA,kBACA,uCACA,mCACA,mCACA,WACE,gDAGF,qJACE,oCACA,+BAGF,qKACE,mDACA,mBAIJ,mJACE,eACA,iBACA,yCAEA,6JACE,sBACA,eAIJ,qJACE,sBACA,eACA,uCACA,eAEA,iKACE,0BAKN,2FACE,sBACA,aACA,mBACA,uBACA,WACA,YACA,YACA,eACA,kBACA,qCAEA,uGACE,oCACA,yCAGF,uGACE,WACA,YACA,oCACA,UACA,8BAIJ,qFACE,eAGE,+IACE,0CASJ,mHACE,MACA,OACA,wBACA,4CACA,2CAEA,iKACE,qBACA,OA6BA,iKACE,SAEA,6KACE,yBAIJ,iIACE,yCAEA,2LACE,uBAIJ,uOACE,wCACA,WACE,mFAGF,qPACE,WACA,kBACA,SACA,YACA,WACA,UAlER,qHACE,MACA,QACA,wBACA,6CACA,2CAEA,mKACE,qBACA,QAIA,mIACE,wCAEA,6LACE,yBAIJ,yOACE,uCACA,WACE,mFAIF,uPACE,WACA,kBACA,SACA,YACA,UACA,UAlCR,yHACE,SACA,OACA,wBACA,6CACA,wCAEA,uKACE,wBACA,OA6BA,uKACE,SAEA,mLACE,yBAIJ,uIACE,yCAEA,iMACE,uBAIJ,6OACE,wCACA,WACE,oFAGF,2PACE,WACA,kBACA,SACA,YACA,WACA,UAlER,2HACE,SACA,QACA,wBACA,8CACA,wCAEA,yKACE,wBACA,QAIA,yIACE,wCAEA,mMACE,yBAIJ,+OACE,uCACA,WACE,oFAIF,6PACE,WACA,kBACA,SACA,YACA,UACA,UAuCV,qHACE,eAGF,+FACE,gBACA,iBACA,gBAEA,mJACE,gBAIJ,+GACE,aAGF,iGACE,aACA,mBACA,uBACA,WACA,YACA,YACA,+BACA,kBACA,eACA,uCACA,wBACA,kBACA,gBAEA,iHACE,WACA,kBACA,MACA,OACA,WACA,YACA,8BACA,UACA,4BACA,kBAGF,yGACE,WACA,YACA,kBACA,UACA,8BAGF,2IACE,uCACA,oCAEA,mJACE,mCACA,8BAKF,qJACE,mCAIJ,mHACE,WACA,mBACA,oBACA,yCAIA,yHACE,sCAKN,iFACE,oBACA,cACA,UACA,YACA,oCAEA,2IACE,sCAGF,+CAXF,iFAYI,sCAEA,6IACE,qCAKN,kBACE,KACE,uBAEF,GACE,0BAIJ,2FACE,iBAGF,6FACE,kBAGF,iGACE,+BACA,eACA,gBACA,sBACA,iBACA,oCAEA,yIACE,kDAIJ,oBACE,GACE,uBAEF,KACE,0BAIJ,sBACE,GACE,0BAEF,IACE,2BAEF,KACE,2BAOJ,iIAr8BA,yXAOF,+KACE,sCACA,kCAEA,2LACE,sCACA,kCAIJ,2LACE,uCA07BA,+CAjtBF,uEA3PE,yXAOF,qHACE,sCACA,kCAEA,iIACE,sCACA,kCAIJ,iIACE,wCAg8BF,uCACE,gKAIE","file":"client.css","sourcesContent":["@use 'sass:map';\n\n$pp-dev-height: 32px;\n$pp-dev-peek: 4px;\n\n// Corner map: v/h — anchored edges; sx — horizontal slide direction for minimize/auto-hide\n// (1 → slides right, -1 → slides left); radius/shadow face the viewport center;\n// edge — hairline border on the exposed horizontal edge.\n$pp-dev-corners: (\n 'top-left': (\n v: top,\n h: left,\n sx: -1,\n radius: 0 0 4px 0,\n shadow: 2px 2px 8px 0 rgba(34, 34, 34, 0.08),\n edge: border-bottom,\n ),\n 'top-right': (\n v: top,\n h: right,\n sx: 1,\n radius: 0 0 0 4px,\n shadow: -2px 2px 8px 0 rgba(34, 34, 34, 0.08),\n edge: border-bottom,\n ),\n 'bottom-left': (\n v: bottom,\n h: left,\n sx: -1,\n radius: 0 4px 0 0,\n shadow: 2px -2px 8px 0 rgba(34, 34, 34, 0.08),\n edge: border-top,\n ),\n 'bottom-right': (\n v: bottom,\n h: right,\n sx: 1,\n radius: 4px 0 0 0,\n shadow: -2px -2px 8px 0 rgba(34, 34, 34, 0.08),\n edge: border-top,\n ),\n);\n\n// Light/dark color maps, shared by the default (light) rules, the prefers-color-scheme:dark\n// media query (Auto), and the explicit data-pp-dev-theme override (Dark/Light picked in the\n// panel's settings popover — see src/client/theme.ts). One place to edit either palette.\n$pp-dev-light-colors: (\n primary: #075b7e,\n secondary: rgba(34, 34, 34, 0.64),\n success: #077e45,\n info: #17a2b8,\n warning: #ffb000,\n danger: #ac2b2b,\n light: #f8f9fa,\n dark: #343a40,\n 'white': #ffffff,\n 'black': #000000,\n);\n$pp-dev-dark-colors: (\n primary: #2a8db5,\n secondary: rgba(220, 220, 220, 0.64),\n success: #0fad5e,\n info: #2dccff,\n warning: #ffb302,\n danger: #e05252,\n light: #343a40,\n dark: #f8f9fa,\n 'white': #000000,\n 'black': #ffffff,\n);\n\n@mixin pp-dev-theme-colors($colors) {\n @each $name, $value in $colors {\n --pp-dev-info-color-#{$name}: #{$value};\n }\n}\n\n// rgba() literals don't follow the --pp-dev-info-color-primary reassignment above,\n// so the button fill/border are re-tinted here to match the dark-mode primary.\n@mixin pp-dev-dark-tints {\n .pp-dev-info__vars-btn {\n background-color: rgba(42, 141, 181, 0.14);\n border-color: rgba(42, 141, 181, 0.45);\n\n &:hover {\n background-color: rgba(42, 141, 181, 0.24);\n border-color: rgba(42, 141, 181, 0.65);\n }\n }\n\n .pp-dev-info__settings-group {\n border-top-color: rgba(255, 255, 255, 0.15);\n }\n}\n\n.pp-dev-info-namespace {\n // Define theme colors — light by default; overridden for Auto (media query, below) or an\n // explicit Dark/Light pick (html[data-pp-dev-theme], below — always wins on specificity).\n @include pp-dev-theme-colors($pp-dev-light-colors);\n\n html[data-pp-dev-theme='light'] & {\n @include pp-dev-theme-colors($pp-dev-light-colors);\n }\n\n .pp-dev-info__popup {\n position: fixed;\n top: 10px;\n right: 10px;\n width: 280px;\n max-width: calc(100vw - 20px);\n height: auto;\n background-color: var(--pp-dev-info-color-white);\n box-shadow: 0 2px 4px rgba(34, 34, 34, 0.2);\n border-radius: 2px;\n display: flex;\n flex-direction: column;\n align-items: flex-end;\n gap: 4px;\n padding: 12px;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n overflow: hidden;\n z-index: 100001;\n border-left: 2px solid var(--pp-dev-info-color-primary);\n transform: translateX(0);\n opacity: 1;\n box-sizing: border-box;\n --pp-dev-info-popup-accent: var(--pp-dev-info-color-primary);\n\n &.entering {\n transform: translateX(calc(100% + 10px));\n opacity: 0;\n }\n\n &.exiting {\n transform: translateX(calc(100% + 10px));\n opacity: 0;\n }\n\n &-title {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 8px;\n padding: 0;\n min-height: 0;\n width: 100%;\n box-sizing: border-box;\n border-bottom: 0;\n color: #040404;\n\n &-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 16px;\n height: 16px;\n flex-shrink: 0;\n color: var(--pp-dev-info-popup-accent);\n }\n\n &-text {\n flex: 1;\n color: #040404;\n font-size: 12px;\n font-weight: 600;\n line-height: 18px;\n text-align: left;\n word-break: break-word;\n }\n\n &-close {\n cursor: pointer;\n min-width: 24px;\n min-height: 24px;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 0;\n border: 0;\n background: none;\n font: inherit;\n color: rgba(34, 34, 34, 0.64);\n border-radius: 2px;\n transition: color 0.2s ease;\n flex-shrink: 0;\n\n &:hover {\n color: var(--pp-dev-info-color-dark);\n }\n\n &:focus-visible {\n outline: 2px solid var(--pp-dev-info-popup-accent);\n outline-offset: 1px;\n }\n\n svg {\n transition: transform 0.2s ease;\n }\n }\n }\n\n &-content {\n margin-left: 24px;\n padding: 0;\n width: calc(100% - 24px);\n overflow-y: auto;\n box-sizing: border-box;\n flex-grow: 0;\n background-color: transparent;\n color: rgba(34, 34, 34, 0.8);\n font-size: 12px;\n font-weight: 400;\n line-height: 18px;\n text-align: left;\n word-break: break-word;\n white-space: pre-line;\n }\n\n &--success {\n --pp-dev-info-popup-accent: var(--pp-dev-info-color-success);\n border-left-color: var(--pp-dev-info-color-success);\n }\n\n &--danger {\n --pp-dev-info-popup-accent: var(--pp-dev-info-color-danger);\n border-left-color: var(--pp-dev-info-color-danger);\n }\n\n &--info {\n --pp-dev-info-popup-accent: var(--pp-dev-info-color-info);\n border-left-color: var(--pp-dev-info-color-info);\n }\n\n &--warning {\n --pp-dev-info-popup-accent: var(--pp-dev-info-color-warning);\n border-left-color: var(--pp-dev-info-color-warning);\n }\n\n // Popups anchored to the left screen edge (panel in a left corner)\n &--left {\n right: auto;\n left: 10px;\n border-left: none;\n border-right: 2px solid var(--pp-dev-info-popup-accent);\n\n &.entering,\n &.exiting {\n transform: translateX(calc(-100% - 10px));\n }\n }\n }\n\n &.pp-dev-info__confirm-overlay,\n .pp-dev-info__confirm-overlay {\n position: fixed;\n inset: 0;\n z-index: 100003;\n display: flex;\n align-items: center;\n justify-content: center;\n background-color: rgba(0, 0, 0, 0.35);\n backdrop-filter: blur(2px);\n -webkit-backdrop-filter: blur(2px);\n }\n\n .pp-dev-info__confirm {\n width: min(408px, calc(100vw - 32px));\n border-radius: 3px;\n border: 1px solid rgba(0, 0, 0, 0.15);\n background-color: var(--pp-dev-info-color-white);\n box-shadow: 0px 8px 32px 0px rgba(34, 34, 34, 0.4);\n overflow: hidden;\n\n &-title {\n padding: 14px 16px;\n font-size: 15px;\n font-weight: 600;\n border-bottom: 1px solid rgba(0, 0, 0, 0.08);\n color: var(--pp-dev-info-color-dark);\n background-color: rgba(0, 0, 0, 0.03);\n }\n\n &-content {\n padding: 16px;\n white-space: pre-wrap;\n color: var(--pp-dev-info-color-dark);\n font-size: 13px;\n line-height: 1.5;\n }\n\n &-actions {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 0 16px 16px;\n }\n\n &-btn {\n width: 100%;\n padding: 8px 12px;\n border-radius: 6px;\n border: 1px solid transparent;\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.2s ease;\n\n &--cancel {\n color: var(--pp-dev-info-color-dark);\n background-color: transparent;\n border-color: rgba(0, 0, 0, 0.2);\n }\n\n &--confirm {\n color: var(--pp-dev-info-color-white);\n background-color: var(--pp-dev-info-color-primary);\n }\n\n &:hover {\n opacity: 0.9;\n }\n }\n }\n\n &.pp-dev-info,\n .pp-dev-info {\n position: fixed;\n z-index: 100000;\n white-space: nowrap;\n width: fit-content;\n height: $pp-dev-height;\n text-align: center;\n padding: 2px;\n gap: 8px;\n background-color: var(--pp-dev-info-color-white);\n color: var(--pp-dev-info-color-dark);\n box-sizing: border-box;\n display: flex;\n align-items: center;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n overflow: visible;\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n\n // Setup default values for styles that can be overlapped by other page styles\n // (e.g. color, font-family, font-size etc.)\n // This is needed to make sure that the extension styles are not overlapped by the page styles\n // and the extension is still usable\n * {\n color: var(--pp-dev-info-color-dark);\n font-family: 'Inter', sans-serif;\n font-size: 12px;\n font-weight: 400;\n line-height: 18px;\n letter-spacing: 0;\n text-decoration: none;\n text-transform: inherit;\n vertical-align: inherit;\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n border: 0;\n outline: 0;\n background: 0 0;\n list-style: none;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n }\n\n &__content {\n display: flex;\n align-items: center;\n gap: 0px;\n }\n\n &__section {\n display: flex;\n align-items: center;\n position: relative;\n padding: 0 8px;\n\n &:not(:last-child)::after {\n content: '';\n position: absolute;\n right: 0;\n top: 50%;\n transform: translateY(-50%);\n height: 16px;\n width: 1px;\n background-color: rgba(34, 34, 34, 0.24);\n\n html[data-pp-dev-theme='dark'] & {\n background-color: rgba(255, 255, 255, 0.2);\n }\n\n @media screen and (prefers-color-scheme: dark) {\n background-color: rgba(255, 255, 255, 0.2);\n\n html[data-pp-dev-theme='light'] & {\n background-color: rgba(34, 34, 34, 0.24);\n }\n }\n }\n\n &:first-child {\n padding-left: 0;\n }\n\n &:last-child {\n padding-right: 0;\n }\n }\n\n &__item {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 12px;\n font-weight: 400;\n line-height: 18px;\n }\n\n &__label {\n color: var(--pp-dev-info-color-secondary);\n font-weight: 400;\n }\n\n &__value {\n font-weight: 600;\n }\n\n &__link {\n color: var(--pp-dev-info-color-primary);\n text-decoration: none;\n transition: color 0.2s ease;\n display: flex;\n align-items: center;\n gap: 8px;\n\n &:hover {\n color: var(--pp-dev-info-color-primary);\n text-decoration: underline;\n }\n }\n\n @mixin bold {\n font-weight: 700;\n }\n\n .primary {\n color: var(--pp-dev-info-color-primary) !important;\n }\n\n .success {\n color: var(--pp-dev-info-color-success) !important;\n }\n\n .info {\n color: var(--pp-dev-info-color-info) !important;\n }\n\n .warning {\n color: var(--pp-dev-info-color-warning) !important;\n }\n\n .danger {\n color: var(--pp-dev-info-color-danger) !important;\n }\n\n a {\n color: var(--pp-dev-info-color-primary);\n text-decoration: none;\n vertical-align: middle;\n line-height: 18px;\n\n &:hover {\n text-decoration: none;\n }\n\n &:active,\n &:hover {\n outline: 0;\n }\n }\n\n button {\n padding: 1px;\n border: none;\n background: none;\n cursor: pointer;\n outline: none;\n }\n\n .bold,\n .status-text {\n @include bold;\n }\n\n &__drag-handle {\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n width: 14px;\n height: 28px;\n cursor: grab;\n touch-action: none;\n border-radius: 3px;\n color: var(--pp-dev-info-color-secondary);\n opacity: 0.55;\n transition:\n opacity 0.2s ease,\n background-color 0.2s ease;\n\n svg {\n pointer-events: none;\n fill: currentColor;\n }\n\n &:hover {\n opacity: 1;\n background-color: rgba(7, 91, 126, 0.06);\n }\n }\n\n &__settings-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n padding: 6px;\n border-radius: 3px;\n cursor: pointer;\n color: var(--pp-dev-info-color-secondary);\n transition: all 0.2s ease;\n\n svg {\n width: 16px;\n height: 16px;\n fill: none;\n transition: transform 0.3s ease;\n }\n\n &:hover {\n color: var(--pp-dev-info-color-primary);\n background-color: rgba(7, 91, 126, 0.06);\n\n svg {\n transform: rotate(45deg);\n }\n }\n }\n\n &__settings {\n position: absolute;\n min-width: 216px;\n padding: 12px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n background-color: var(--pp-dev-info-color-white);\n border: 1px solid rgba(34, 34, 34, 0.15);\n border-radius: 3px;\n box-shadow: 0 4px 16px rgba(34, 34, 34, 0.2);\n z-index: 100002;\n cursor: default;\n white-space: normal;\n text-align: left;\n\n .pp-dev-info__settings-title {\n font-size: 10px;\n font-weight: 600;\n color: var(--pp-dev-info-color-secondary);\n text-transform: uppercase;\n letter-spacing: 0.4px;\n }\n\n .pp-dev-info__settings-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n }\n\n .pp-dev-info__settings-toggle {\n appearance: auto;\n -webkit-appearance: checkbox;\n width: 14px;\n height: 14px;\n accent-color: var(--pp-dev-info-color-primary);\n cursor: pointer;\n flex-shrink: 0;\n }\n\n .pp-dev-info__corner-grid {\n display: grid;\n grid-template-columns: repeat(2, 30px);\n gap: 4px;\n }\n\n .pp-dev-info__corner-btn {\n width: 30px;\n height: 22px;\n border: 1px solid rgba(34, 34, 34, 0.25);\n border-radius: 3px;\n cursor: pointer;\n position: relative;\n background: transparent;\n transition: border-color 0.2s ease;\n\n &::after {\n content: '';\n position: absolute;\n width: 10px;\n height: 6px;\n border-radius: 1px;\n background-color: var(--pp-dev-info-color-secondary);\n transition: background-color 0.2s ease;\n }\n\n &--top-left::after {\n top: 2px;\n left: 2px;\n }\n\n &--top-right::after {\n top: 2px;\n right: 2px;\n }\n\n &--bottom-left::after {\n bottom: 2px;\n left: 2px;\n }\n\n &--bottom-right::after {\n bottom: 2px;\n right: 2px;\n }\n\n &:hover {\n border-color: var(--pp-dev-info-color-primary);\n }\n\n &.active {\n border-color: var(--pp-dev-info-color-primary);\n\n &::after {\n background-color: var(--pp-dev-info-color-primary);\n }\n }\n }\n\n .pp-dev-info__theme-grid {\n display: flex;\n gap: 4px;\n }\n\n .pp-dev-info__theme-btn {\n padding: 3px 8px;\n border: 1px solid rgba(34, 34, 34, 0.25);\n border-radius: 3px;\n cursor: pointer;\n background: transparent;\n font-size: 11px;\n line-height: 16px;\n color: var(--pp-dev-info-color-secondary);\n transition:\n border-color 0.2s ease,\n color 0.2s ease,\n background-color 0.2s ease;\n\n &:hover {\n border-color: var(--pp-dev-info-color-primary);\n color: var(--pp-dev-info-color-primary);\n }\n\n &.active {\n border-color: var(--pp-dev-info-color-primary);\n background-color: var(--pp-dev-info-color-primary);\n color: var(--pp-dev-info-color-white);\n }\n\n &:focus-visible {\n outline: 2px solid var(--pp-dev-info-color-primary);\n outline-offset: 1px;\n }\n }\n\n .pp-dev-info__settings-hide-btn {\n width: 100%;\n padding: 6px 10px;\n border: 1px solid rgba(34, 34, 34, 0.2);\n border-radius: 4px;\n cursor: pointer;\n font-weight: 600;\n color: var(--pp-dev-info-color-danger);\n transition: background-color 0.2s ease;\n\n &:hover {\n background-color: rgba(172, 43, 43, 0.06);\n }\n }\n\n .pp-dev-info__settings-group {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding-top: 10px;\n border-top: 1px solid rgba(34, 34, 34, 0.12);\n }\n\n .pp-dev-info__vars-buttons {\n display: flex;\n flex-direction: column;\n gap: 6px;\n }\n\n .pp-dev-info__vars-btn {\n width: 100%;\n padding: 6px 10px;\n border-radius: 4px;\n cursor: pointer;\n font-weight: 600;\n text-align: center;\n color: var(--pp-dev-info-color-primary);\n background-color: rgba(7, 91, 126, 0.1);\n border: 1px solid rgba(7, 91, 126, 0.4);\n transition:\n background-color 0.2s ease,\n border-color 0.2s ease;\n\n &:hover {\n background-color: rgba(7, 91, 126, 0.18);\n border-color: rgba(7, 91, 126, 0.6);\n }\n\n &:focus-visible {\n outline: 2px solid var(--pp-dev-info-color-primary);\n outline-offset: 1px;\n }\n }\n\n .pp-dev-info__settings-hint {\n font-size: 10px;\n line-height: 14px;\n color: var(--pp-dev-info-color-secondary);\n\n code {\n font-family: monospace;\n font-size: 10px;\n }\n }\n\n .pp-dev-info__settings-reset {\n align-self: flex-start;\n font-size: 11px;\n color: var(--pp-dev-info-color-primary);\n cursor: pointer;\n\n &:hover {\n text-decoration: underline;\n }\n }\n }\n\n &__wrap-btn {\n box-sizing: border-box;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n padding: 6px;\n cursor: pointer;\n border-radius: 3px;\n transition: background-color 0.2s ease;\n\n &:hover {\n background-color: rgba(7, 91, 126, 0.06);\n animation: arrowHover 0.8s linear infinite;\n }\n\n &-arrow {\n width: 16px;\n height: 16px;\n color: var(--pp-dev-info-color-dark);\n fill: none;\n transition: transform 0.2s ease;\n }\n }\n\n &.closed {\n z-index: 100002;\n\n & .pp-dev-info__wrap-btn {\n &:hover {\n animation: arrowHover 1.2s linear infinite;\n }\n }\n }\n\n // Corner variants: placement, center-facing radius/shadow, direction-aware\n // minimize (.closed) and auto-hide slides. The corner class is always rendered\n // on the panel root (server default: bottom-right).\n @each $name, $c in $pp-dev-corners {\n &.pp-dev-info--#{$name} {\n #{map.get($c, v)}: 0;\n #{map.get($c, h)}: 0;\n border-radius: map.get($c, radius);\n box-shadow: map.get($c, shadow);\n #{map.get($c, edge)}: 1px solid rgba(34, 34, 34, 0.08);\n\n .pp-dev-info__settings {\n #{map.get($c, v)}: calc(100% + 8px);\n #{map.get($c, h)}: 0;\n }\n\n @if map.get($c, sx) == 1 {\n &.closed {\n transform: translateX(calc(100% - $pp-dev-height));\n\n .pp-dev-info__wrap-btn-arrow {\n transform: rotate(180deg);\n }\n }\n\n &.pp-dev-info--auto-hide:not(.is-peeking):not(.is-dragging) {\n transform: translateX(calc(100% - $pp-dev-peek));\n box-shadow:\n map.get($c, shadow),\n inset $pp-dev-peek 0 0 0 var(--pp-dev-info-color-primary);\n\n // Extended invisible hover zone on the inner side of the 4px strip\n &::after {\n content: '';\n position: absolute;\n top: -6px;\n bottom: -6px;\n left: -8px;\n width: 8px;\n }\n }\n } @else {\n .pp-dev-info__wrap-btn {\n order: 99;\n\n &-arrow {\n transform: rotate(180deg);\n }\n }\n\n &.closed {\n transform: translateX(calc(-100% + $pp-dev-height));\n\n .pp-dev-info__wrap-btn-arrow {\n transform: rotate(0deg);\n }\n }\n\n &.pp-dev-info--auto-hide:not(.is-peeking):not(.is-dragging) {\n transform: translateX(calc(-100% + $pp-dev-peek));\n box-shadow:\n map.get($c, shadow),\n inset (-$pp-dev-peek) 0 0 0 var(--pp-dev-info-color-primary);\n\n &::after {\n content: '';\n position: absolute;\n top: -6px;\n bottom: -6px;\n right: -8px;\n width: 8px;\n }\n }\n }\n }\n }\n\n &.pp-dev-info--auto-hide {\n z-index: 100002;\n }\n\n &.is-dragging {\n transition: none;\n user-select: none;\n cursor: grabbing;\n\n .pp-dev-info__drag-handle {\n cursor: grabbing;\n }\n }\n\n &.pp-dev-info--hidden {\n display: none;\n }\n\n .sync-button {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n padding: 6px;\n background-color: transparent;\n border-radius: 3px;\n cursor: pointer;\n color: var(--pp-dev-info-color-primary);\n transition: all 0.2s ease;\n position: relative;\n overflow: hidden;\n\n &::before {\n content: '';\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background-color: currentColor;\n opacity: 0;\n transition: opacity 0.2s ease;\n border-radius: 3px;\n }\n\n svg {\n width: 16px;\n height: 16px;\n position: relative;\n z-index: 1;\n transition: transform 0.3s ease;\n }\n\n &:hover:not(.disabled) {\n color: var(--pp-dev-info-color-primary);\n background-color: rgba(7, 91, 126, 0.06);\n\n svg {\n transform: scale(1.1) rotate(45deg);\n transition: transform 0.2s ease;\n }\n }\n\n &:active:not(.disabled) {\n svg {\n transform: scale(0.9) rotate(45deg);\n }\n }\n\n &.disabled {\n opacity: 0.5;\n cursor: not-allowed;\n pointer-events: none;\n color: var(--pp-dev-info-color-secondary);\n }\n\n &.syncing {\n svg {\n animation: rotate 1.5s linear infinite;\n }\n }\n }\n\n &__sep {\n display: inline-flex;\n flex-shrink: 0;\n width: 1px;\n height: 16px;\n background-color: rgba(34, 34, 34, 0.24);\n\n html[data-pp-dev-theme='dark'] & {\n background-color: rgba(255, 255, 255, 0.2);\n }\n\n @media screen and (prefers-color-scheme: dark) {\n background-color: rgba(255, 255, 255, 0.2);\n\n html[data-pp-dev-theme='light'] & {\n background-color: rgba(34, 34, 34, 0.24);\n }\n }\n }\n\n @keyframes rotate {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n }\n\n .margin-5 {\n margin-right: 5px;\n }\n\n .margin-10 {\n margin-right: 10px;\n }\n\n .custom-font {\n font-family: 'Inter', sans-serif;\n font-size: 13px;\n font-weight: 400;\n vertical-align: middle;\n line-height: 18px;\n color: var(--pp-dev-info-color-dark);\n\n &:not(.not-colored) a {\n color: var(--pp-dev-info-color-primary) !important;\n }\n }\n\n @keyframes rotation {\n 0% {\n transform: rotate(0deg);\n }\n 100% {\n transform: rotate(360deg);\n }\n }\n\n @keyframes arrowHover {\n 0% {\n transform: translateX(2px);\n }\n 50% {\n transform: translateX(-2px);\n }\n 100% {\n transform: translateX(2px);\n }\n }\n\n // Explicit Dark pick (html[data-pp-dev-theme='dark'], set via the settings popover's Theme\n // row — see src/client/theme.ts) — always wins over both the light default above and the\n // Auto media query below, on selector specificity alone.\n html[data-pp-dev-theme='dark'] & {\n @include pp-dev-theme-colors($pp-dev-dark-colors);\n @include pp-dev-dark-tints;\n }\n\n // Auto (OS/browser preference) — same values, only takes effect when no explicit\n // html[data-pp-dev-theme] override is set (those always outrank a plain :root/& selector).\n @media screen and (prefers-color-scheme: dark) {\n @include pp-dev-theme-colors($pp-dev-dark-colors);\n @include pp-dev-dark-tints;\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n &.pp-dev-info,\n .pp-dev-info,\n .pp-dev-info__popup,\n .pp-dev-info__settings {\n transition: none;\n }\n }\n}\n"]}
1
+ {"version":3,"sourceRoot":"","sources":["client.scss"],"names":[],"mappings":"AA+FA,uBAtBI,sXA2BF,qDA3BE,sXA+BF,2CACE,eACA,SACA,WACA,YACA,6BACA,YACA,gDACA,uCACA,kBACA,aACA,sBACA,qBACA,QACA,aACA,gDACA,gBACA,eACA,uDACA,wBACA,UACA,sBACA,6DAEA,oDACE,wCACA,UAGF,mDACE,wCACA,UAGF,iDACE,aACA,uBACA,8BACA,QACA,UACA,aACA,WACA,sBACA,gBACA,cAEA,sDACE,aACA,mBACA,uBACA,WACA,YACA,cACA,sCAGF,sDACE,OACA,cACA,eACA,gBACA,iBACA,gBACA,sBAGF,uDACE,eACA,eACA,gBACA,aACA,mBACA,uBACA,UACA,SACA,gBACA,aACA,yBACA,kBACA,0BACA,cAEA,6DACE,oCAGF,qEACE,kDACA,mBAGF,2DACE,8BAKN,mDACE,iBACA,UACA,wBACA,gBACA,sBACA,YACA,+BACA,wBACA,eACA,gBACA,iBACA,gBACA,sBACA,qBAGF,oDACE,6DACA,mDAGF,mDACE,4DACA,kDAGF,iDACE,0DACA,gDAGF,oDACE,6DACA,mDAIF,iDACE,WACA,UACA,iBACA,uDAEA,mHAEE,yCAKN,yGAEE,eACA,QACA,eACA,aACA,mBACA,uBACA,iCACA,0BACA,kCAGF,6CACE,8BACA,kBACA,iCACA,gDACA,8CACA,gBAEA,mDACE,kBACA,eACA,gBACA,wCACA,oCACA,iCAGF,qDACE,aACA,qBACA,oCACA,eACA,gBAGF,qDACE,aACA,sBACA,QACA,oBAGF,iDACE,WACA,iBACA,kBACA,+BACA,eACA,gBACA,eACA,wBAEA,yDACE,oCACA,+BACA,4BAGF,0DACE,qCACA,kDAGF,uDACE,WAKN,uEAEE,eACA,eACA,mBACA,kBACA,OAxUY,KAyUZ,kBACA,YACA,QACA,gDACA,oCACA,sBACA,aACA,mBACA,gDACA,iBACA,0BACA,kCAMA,2EACE,oCACA,+BACA,eACA,gBACA,iBACA,iBACA,qBACA,uBACA,uBACA,sBACA,SACA,UACA,SACA,UACA,eACA,gBACA,mCACA,kCAGF,yFACE,aACA,mBACA,QAGF,yFACE,aACA,mBACA,kBACA,cAEA,yIACE,WACA,kBACA,QACA,QACA,2BACA,YACA,UACA,oCAEA,mMACE,sCAGF,+CAdF,yIAeI,sCAEA,qMACE,qCAKN,iHACE,eAGF,+GACE,gBAIJ,mFACE,aACA,mBACA,QACA,eACA,gBACA,iBAGF,qFACE,yCACA,gBAGF,qFACE,gBAGF,mFACE,uCACA,qBACA,0BACA,aACA,mBACA,QAEA,+FACE,uCACA,0BAQJ,yFACE,kDAGF,yFACE,kDAGF,mFACE,+CAGF,yFACE,kDAGF,uFACE,iDAGF,2EACE,uCACA,qBACA,sBACA,iBAEA,uFACE,qBAGF,gLAEE,UAIJ,qFACE,YACA,YACA,gBACA,eACA,aAGF,oLA/CE,gBAoDF,iGACE,aACA,mBACA,uBACA,cACA,WACA,YACA,YACA,kBACA,kBACA,yCACA,YACA,WACE,2CAGF,yGACE,oBACA,kBAGF,6GACE,UACA,oCAIJ,mGACE,aACA,mBACA,uBACA,WACA,YACA,YACA,kBACA,eACA,yCACA,wBAEA,2GACE,WACA,YACA,UACA,8BAGF,+GACE,uCACA,oCAEA,uHACE,wBAKN,2FACE,kBACA,gBACA,aACA,aACA,sBACA,SACA,gDACA,oCACA,kBACA,wCACA,eACA,eACA,mBACA,gBAEA,qJACE,eACA,gBACA,yCACA,yBACA,oBAGF,iJACE,aACA,mBACA,8BACA,SAGF,uJACE,gBACA,4BACA,WACA,YACA,8CACA,eACA,cAGF,+IACE,aACA,sCACA,QAGF,6IACE,WACA,YACA,oCACA,kBACA,eACA,kBACA,yBACA,iCAEA,2JACE,WACA,kBACA,WACA,WACA,kBACA,oDACA,qCAGF,+KACE,QACA,SAGF,iLACE,QACA,UAGF,qLACE,WACA,SAGF,uLACE,WACA,UAGF,yJACE,8CAGF,2JACE,8CAEA,yKACE,kDAKN,6IACE,aACA,QAGF,2IACE,gBACA,oCACA,kBACA,eACA,yBACA,eACA,iBACA,yCACA,WACE,+DAIF,uJACE,8CACA,uCAGF,yJACE,8CACA,kDACA,qCAGF,uKACE,mDACA,mBAIJ,2JACE,WACA,iBACA,mCACA,kBACA,eACA,gBACA,sCACA,qCAEA,uKACE,qCAIJ,qJACE,aACA,sBACA,QACA,iBACA,wCAGF,iJACE,aACA,sBACA,QAGF,yIACE,WACA,iBACA,kBACA,eACA,gBACA,kBACA,uCACA,mCACA,mCACA,WACE,gDAGF,qJACE,oCACA,+BAGF,qKACE,mDACA,mBAIJ,mJACE,eACA,iBACA,yCAEA,6JACE,sBACA,eAIJ,qJACE,sBACA,eACA,uCACA,eAEA,iKACE,0BAMN,6FACE,aACA,mBACA,uBACA,WACA,YACA,YACA,kBACA,eACA,yCACA,wBAEA,qGACE,WACA,YACA,cAGF,yGACE,uCACA,oCAKJ,mKACE,WACA,yBAEA,+KACE,yBAIJ,iIACE,YACA,6BACA,sBAEA,iLACE,eACA,iBACA,oCAEA,2LACE,cACA,eACA,yCAIJ,mLACE,aACA,sBACA,QACA,iBACA,gBAGF,iLACE,aACA,mBACA,QACA,gBACA,kBACA,oCAEA,uMACE,0BACA,sCAIJ,yLACE,gBACA,yBACA,WACA,YACA,SACA,cACA,8CACA,eAEA,2MACE,mBAIJ,iLACE,aACA,sBACA,YACA,OAGF,mLACE,eACA,gBACA,oCACA,gBACA,uBAGF,mLACE,eACA,iBACA,yCACA,gBACA,uBAGF,6MACE,sCAGF,uLACE,cACA,gBACA,oCACA,kBACA,yBACA,eACA,eACA,iBACA,uCAEA,mMACE,8CAGF,uMACE,8CACA,kDACA,qCAIJ,uLACE,cACA,WACA,YACA,UACA,YACA,kBACA,yBACA,eACA,eACA,iBACA,yCAEA,mMACE,sCACA,qCAIJ,qLACE,WACA,sBACA,gBACA,oCACA,kBACA,yBACA,eACA,oCAEA,iMACE,aACA,8CAIJ,sXAEE,eACA,iBACA,sCAGF,iMACE,yCAGF,mJACE,wBAIJ,2FACE,sBACA,aACA,mBACA,uBACA,WACA,YACA,YACA,eACA,kBACA,qCAEA,uGACE,oCACA,yCAGF,uGACE,WACA,YACA,oCACA,UACA,8BAIJ,qFACE,eAGE,+IACE,0CASJ,mHACE,MACA,OACA,wBACA,4CACA,2CAEA,iKACE,qBACA,OA6BA,iKACE,SAEA,6KACE,yBAIJ,iIACE,yCAEA,2LACE,uBAIJ,uOACE,wCACA,WACE,mFAGF,qPACE,WACA,kBACA,SACA,YACA,WACA,UAlER,qHACE,MACA,QACA,wBACA,6CACA,2CAEA,mKACE,qBACA,QAIA,mIACE,wCAEA,6LACE,yBAIJ,yOACE,uCACA,WACE,mFAIF,uPACE,WACA,kBACA,SACA,YACA,UACA,UAlCR,yHACE,SACA,OACA,wBACA,6CACA,wCAEA,uKACE,wBACA,OA6BA,uKACE,SAEA,mLACE,yBAIJ,uIACE,yCAEA,iMACE,uBAIJ,6OACE,wCACA,WACE,oFAGF,2PACE,WACA,kBACA,SACA,YACA,WACA,UAlER,2HACE,SACA,QACA,wBACA,8CACA,wCAEA,yKACE,wBACA,QAIA,yIACE,wCAEA,mMACE,yBAIJ,+OACE,uCACA,WACE,oFAIF,6PACE,WACA,kBACA,SACA,YACA,UACA,UAuCV,qHACE,eAGF,+FACE,gBACA,iBACA,gBAEA,mJACE,gBAIJ,+GACE,aAGF,iGACE,aACA,mBACA,uBACA,WACA,YACA,YACA,+BACA,kBACA,eACA,uCACA,wBACA,kBACA,gBAEA,iHACE,WACA,kBACA,MACA,OACA,WACA,YACA,8BACA,UACA,4BACA,kBAGF,yGACE,WACA,YACA,kBACA,UACA,8BAGF,2IACE,uCACA,oCAEA,mJACE,mCACA,8BAKF,qJACE,mCAIJ,mHACE,WACA,mBACA,oBACA,yCAIA,yHACE,sCAKN,iFACE,oBACA,cACA,UACA,YACA,oCAEA,2IACE,sCAGF,+CAXF,iFAYI,sCAEA,6IACE,qCAKN,kBACE,KACE,uBAEF,GACE,0BAIJ,2FACE,iBAGF,6FACE,kBAGF,iGACE,+BACA,eACA,gBACA,sBACA,iBACA,oCAEA,yIACE,kDAIJ,oBACE,GACE,uBAEF,KACE,0BAIJ,sBACE,GACE,0BAEF,IACE,2BAEF,KACE,2BAOJ,iIAloCA,yXAOF,+KACE,sCACA,kCAEA,2LACE,sCACA,kCAIJ,2LACE,uCAunCA,+CA94BF,uEA3PE,yXAOF,qHACE,sCACA,kCAEA,iIACE,sCACA,kCAIJ,iIACE,wCA6nCF,uCACE,gKAIE","file":"client.css","sourcesContent":["@use 'sass:map';\n\n$pp-dev-height: 32px;\n$pp-dev-peek: 4px;\n\n// Corner map: v/h — anchored edges; sx — horizontal slide direction for minimize/auto-hide\n// (1 → slides right, -1 → slides left); radius/shadow face the viewport center;\n// edge — hairline border on the exposed horizontal edge.\n$pp-dev-corners: (\n 'top-left': (\n v: top,\n h: left,\n sx: -1,\n radius: 0 0 4px 0,\n shadow: 2px 2px 8px 0 rgba(34, 34, 34, 0.08),\n edge: border-bottom,\n ),\n 'top-right': (\n v: top,\n h: right,\n sx: 1,\n radius: 0 0 0 4px,\n shadow: -2px 2px 8px 0 rgba(34, 34, 34, 0.08),\n edge: border-bottom,\n ),\n 'bottom-left': (\n v: bottom,\n h: left,\n sx: -1,\n radius: 0 4px 0 0,\n shadow: 2px -2px 8px 0 rgba(34, 34, 34, 0.08),\n edge: border-top,\n ),\n 'bottom-right': (\n v: bottom,\n h: right,\n sx: 1,\n radius: 4px 0 0 0,\n shadow: -2px -2px 8px 0 rgba(34, 34, 34, 0.08),\n edge: border-top,\n ),\n);\n\n// Light/dark color maps, shared by the default (light) rules, the prefers-color-scheme:dark\n// media query (Auto), and the explicit data-pp-dev-theme override (Dark/Light picked in the\n// panel's settings popover — see src/client/theme.ts). One place to edit either palette.\n$pp-dev-light-colors: (\n primary: #075b7e,\n secondary: rgba(34, 34, 34, 0.64),\n success: #077e45,\n info: #17a2b8,\n warning: #ffb000,\n danger: #ac2b2b,\n light: #f8f9fa,\n dark: #343a40,\n 'white': #ffffff,\n 'black': #000000,\n);\n$pp-dev-dark-colors: (\n primary: #2a8db5,\n secondary: rgba(220, 220, 220, 0.64),\n success: #0fad5e,\n info: #2dccff,\n warning: #ffb302,\n danger: #e05252,\n light: #343a40,\n dark: #f8f9fa,\n 'white': #000000,\n 'black': #ffffff,\n);\n\n@mixin pp-dev-theme-colors($colors) {\n @each $name, $value in $colors {\n --pp-dev-info-color-#{$name}: #{$value};\n }\n}\n\n// rgba() literals don't follow the --pp-dev-info-color-primary reassignment above,\n// so the button fill/border are re-tinted here to match the dark-mode primary.\n@mixin pp-dev-dark-tints {\n .pp-dev-info__vars-btn {\n background-color: rgba(42, 141, 181, 0.14);\n border-color: rgba(42, 141, 181, 0.45);\n\n &:hover {\n background-color: rgba(42, 141, 181, 0.24);\n border-color: rgba(42, 141, 181, 0.65);\n }\n }\n\n .pp-dev-info__settings-group {\n border-top-color: rgba(255, 255, 255, 0.15);\n }\n}\n\n.pp-dev-info-namespace {\n // Define theme colors — light by default; overridden for Auto (media query, below) or an\n // explicit Dark/Light pick (html[data-pp-dev-theme], below — always wins on specificity).\n @include pp-dev-theme-colors($pp-dev-light-colors);\n\n html[data-pp-dev-theme='light'] & {\n @include pp-dev-theme-colors($pp-dev-light-colors);\n }\n\n .pp-dev-info__popup {\n position: fixed;\n top: 10px;\n right: 10px;\n width: 280px;\n max-width: calc(100vw - 20px);\n height: auto;\n background-color: var(--pp-dev-info-color-white);\n box-shadow: 0 2px 4px rgba(34, 34, 34, 0.2);\n border-radius: 2px;\n display: flex;\n flex-direction: column;\n align-items: flex-end;\n gap: 4px;\n padding: 12px;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n overflow: hidden;\n z-index: 100001;\n border-left: 2px solid var(--pp-dev-info-color-primary);\n transform: translateX(0);\n opacity: 1;\n box-sizing: border-box;\n --pp-dev-info-popup-accent: var(--pp-dev-info-color-primary);\n\n &.entering {\n transform: translateX(calc(100% + 10px));\n opacity: 0;\n }\n\n &.exiting {\n transform: translateX(calc(100% + 10px));\n opacity: 0;\n }\n\n &-title {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 8px;\n padding: 0;\n min-height: 0;\n width: 100%;\n box-sizing: border-box;\n border-bottom: 0;\n color: #040404;\n\n &-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 16px;\n height: 16px;\n flex-shrink: 0;\n color: var(--pp-dev-info-popup-accent);\n }\n\n &-text {\n flex: 1;\n color: #040404;\n font-size: 12px;\n font-weight: 600;\n line-height: 18px;\n text-align: left;\n word-break: break-word;\n }\n\n &-close {\n cursor: pointer;\n min-width: 24px;\n min-height: 24px;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 0;\n border: 0;\n background: none;\n font: inherit;\n color: rgba(34, 34, 34, 0.64);\n border-radius: 2px;\n transition: color 0.2s ease;\n flex-shrink: 0;\n\n &:hover {\n color: var(--pp-dev-info-color-dark);\n }\n\n &:focus-visible {\n outline: 2px solid var(--pp-dev-info-popup-accent);\n outline-offset: 1px;\n }\n\n svg {\n transition: transform 0.2s ease;\n }\n }\n }\n\n &-content {\n margin-left: 24px;\n padding: 0;\n width: calc(100% - 24px);\n overflow-y: auto;\n box-sizing: border-box;\n flex-grow: 0;\n background-color: transparent;\n color: rgba(34, 34, 34, 0.8);\n font-size: 12px;\n font-weight: 400;\n line-height: 18px;\n text-align: left;\n word-break: break-word;\n white-space: pre-line;\n }\n\n &--success {\n --pp-dev-info-popup-accent: var(--pp-dev-info-color-success);\n border-left-color: var(--pp-dev-info-color-success);\n }\n\n &--danger {\n --pp-dev-info-popup-accent: var(--pp-dev-info-color-danger);\n border-left-color: var(--pp-dev-info-color-danger);\n }\n\n &--info {\n --pp-dev-info-popup-accent: var(--pp-dev-info-color-info);\n border-left-color: var(--pp-dev-info-color-info);\n }\n\n &--warning {\n --pp-dev-info-popup-accent: var(--pp-dev-info-color-warning);\n border-left-color: var(--pp-dev-info-color-warning);\n }\n\n // Popups anchored to the left screen edge (panel in a left corner)\n &--left {\n right: auto;\n left: 10px;\n border-left: none;\n border-right: 2px solid var(--pp-dev-info-popup-accent);\n\n &.entering,\n &.exiting {\n transform: translateX(calc(-100% - 10px));\n }\n }\n }\n\n &.pp-dev-info__confirm-overlay,\n .pp-dev-info__confirm-overlay {\n position: fixed;\n inset: 0;\n z-index: 100003;\n display: flex;\n align-items: center;\n justify-content: center;\n background-color: rgba(0, 0, 0, 0.35);\n backdrop-filter: blur(2px);\n -webkit-backdrop-filter: blur(2px);\n }\n\n .pp-dev-info__confirm {\n width: min(408px, calc(100vw - 32px));\n border-radius: 3px;\n border: 1px solid rgba(0, 0, 0, 0.15);\n background-color: var(--pp-dev-info-color-white);\n box-shadow: 0px 8px 32px 0px rgba(34, 34, 34, 0.4);\n overflow: hidden;\n\n &-title {\n padding: 14px 16px;\n font-size: 15px;\n font-weight: 600;\n border-bottom: 1px solid rgba(0, 0, 0, 0.08);\n color: var(--pp-dev-info-color-dark);\n background-color: rgba(0, 0, 0, 0.03);\n }\n\n &-content {\n padding: 16px;\n white-space: pre-wrap;\n color: var(--pp-dev-info-color-dark);\n font-size: 13px;\n line-height: 1.5;\n }\n\n &-actions {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 0 16px 16px;\n }\n\n &-btn {\n width: 100%;\n padding: 8px 12px;\n border-radius: 6px;\n border: 1px solid transparent;\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.2s ease;\n\n &--cancel {\n color: var(--pp-dev-info-color-dark);\n background-color: transparent;\n border-color: rgba(0, 0, 0, 0.2);\n }\n\n &--confirm {\n color: var(--pp-dev-info-color-white);\n background-color: var(--pp-dev-info-color-primary);\n }\n\n &:hover {\n opacity: 0.9;\n }\n }\n }\n\n &.pp-dev-info,\n .pp-dev-info {\n position: fixed;\n z-index: 100000;\n white-space: nowrap;\n width: fit-content;\n height: $pp-dev-height;\n text-align: center;\n padding: 2px;\n gap: 8px;\n background-color: var(--pp-dev-info-color-white);\n color: var(--pp-dev-info-color-dark);\n box-sizing: border-box;\n display: flex;\n align-items: center;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n overflow: visible;\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n\n // Setup default values for styles that can be overlapped by other page styles\n // (e.g. color, font-family, font-size etc.)\n // This is needed to make sure that the extension styles are not overlapped by the page styles\n // and the extension is still usable\n * {\n color: var(--pp-dev-info-color-dark);\n font-family: 'Inter', sans-serif;\n font-size: 12px;\n font-weight: 400;\n line-height: 18px;\n letter-spacing: 0;\n text-decoration: none;\n text-transform: inherit;\n vertical-align: inherit;\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n border: 0;\n outline: 0;\n background: 0 0;\n list-style: none;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n }\n\n &__content {\n display: flex;\n align-items: center;\n gap: 0px;\n }\n\n &__section {\n display: flex;\n align-items: center;\n position: relative;\n padding: 0 8px;\n\n &:not(:last-child)::after {\n content: '';\n position: absolute;\n right: 0;\n top: 50%;\n transform: translateY(-50%);\n height: 16px;\n width: 1px;\n background-color: rgba(34, 34, 34, 0.24);\n\n html[data-pp-dev-theme='dark'] & {\n background-color: rgba(255, 255, 255, 0.2);\n }\n\n @media screen and (prefers-color-scheme: dark) {\n background-color: rgba(255, 255, 255, 0.2);\n\n html[data-pp-dev-theme='light'] & {\n background-color: rgba(34, 34, 34, 0.24);\n }\n }\n }\n\n &:first-child {\n padding-left: 0;\n }\n\n &:last-child {\n padding-right: 0;\n }\n }\n\n &__item {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 12px;\n font-weight: 400;\n line-height: 18px;\n }\n\n &__label {\n color: var(--pp-dev-info-color-secondary);\n font-weight: 400;\n }\n\n &__value {\n font-weight: 600;\n }\n\n &__link {\n color: var(--pp-dev-info-color-primary);\n text-decoration: none;\n transition: color 0.2s ease;\n display: flex;\n align-items: center;\n gap: 8px;\n\n &:hover {\n color: var(--pp-dev-info-color-primary);\n text-decoration: underline;\n }\n }\n\n @mixin bold {\n font-weight: 700;\n }\n\n .primary {\n color: var(--pp-dev-info-color-primary) !important;\n }\n\n .success {\n color: var(--pp-dev-info-color-success) !important;\n }\n\n .info {\n color: var(--pp-dev-info-color-info) !important;\n }\n\n .warning {\n color: var(--pp-dev-info-color-warning) !important;\n }\n\n .danger {\n color: var(--pp-dev-info-color-danger) !important;\n }\n\n a {\n color: var(--pp-dev-info-color-primary);\n text-decoration: none;\n vertical-align: middle;\n line-height: 18px;\n\n &:hover {\n text-decoration: none;\n }\n\n &:active,\n &:hover {\n outline: 0;\n }\n }\n\n button {\n padding: 1px;\n border: none;\n background: none;\n cursor: pointer;\n outline: none;\n }\n\n .bold,\n .status-text {\n @include bold;\n }\n\n &__drag-handle {\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n width: 14px;\n height: 28px;\n cursor: grab;\n touch-action: none;\n border-radius: 3px;\n color: var(--pp-dev-info-color-secondary);\n opacity: 0.55;\n transition:\n opacity 0.2s ease,\n background-color 0.2s ease;\n\n svg {\n pointer-events: none;\n fill: currentColor;\n }\n\n &:hover {\n opacity: 1;\n background-color: rgba(7, 91, 126, 0.06);\n }\n }\n\n &__settings-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n padding: 6px;\n border-radius: 3px;\n cursor: pointer;\n color: var(--pp-dev-info-color-secondary);\n transition: all 0.2s ease;\n\n svg {\n width: 16px;\n height: 16px;\n fill: none;\n transition: transform 0.3s ease;\n }\n\n &:hover {\n color: var(--pp-dev-info-color-primary);\n background-color: rgba(7, 91, 126, 0.06);\n\n svg {\n transform: rotate(45deg);\n }\n }\n }\n\n &__settings {\n position: absolute;\n min-width: 216px;\n padding: 12px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n background-color: var(--pp-dev-info-color-white);\n border: 1px solid rgba(34, 34, 34, 0.15);\n border-radius: 3px;\n box-shadow: 0 4px 16px rgba(34, 34, 34, 0.2);\n z-index: 100002;\n cursor: default;\n white-space: normal;\n text-align: left;\n\n .pp-dev-info__settings-title {\n font-size: 10px;\n font-weight: 600;\n color: var(--pp-dev-info-color-secondary);\n text-transform: uppercase;\n letter-spacing: 0.4px;\n }\n\n .pp-dev-info__settings-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n }\n\n .pp-dev-info__settings-toggle {\n appearance: auto;\n -webkit-appearance: checkbox;\n width: 14px;\n height: 14px;\n accent-color: var(--pp-dev-info-color-primary);\n cursor: pointer;\n flex-shrink: 0;\n }\n\n .pp-dev-info__corner-grid {\n display: grid;\n grid-template-columns: repeat(2, 30px);\n gap: 4px;\n }\n\n .pp-dev-info__corner-btn {\n width: 30px;\n height: 22px;\n border: 1px solid rgba(34, 34, 34, 0.25);\n border-radius: 3px;\n cursor: pointer;\n position: relative;\n background: transparent;\n transition: border-color 0.2s ease;\n\n &::after {\n content: '';\n position: absolute;\n width: 10px;\n height: 6px;\n border-radius: 1px;\n background-color: var(--pp-dev-info-color-secondary);\n transition: background-color 0.2s ease;\n }\n\n &--top-left::after {\n top: 2px;\n left: 2px;\n }\n\n &--top-right::after {\n top: 2px;\n right: 2px;\n }\n\n &--bottom-left::after {\n bottom: 2px;\n left: 2px;\n }\n\n &--bottom-right::after {\n bottom: 2px;\n right: 2px;\n }\n\n &:hover {\n border-color: var(--pp-dev-info-color-primary);\n }\n\n &.active {\n border-color: var(--pp-dev-info-color-primary);\n\n &::after {\n background-color: var(--pp-dev-info-color-primary);\n }\n }\n }\n\n .pp-dev-info__theme-grid {\n display: flex;\n gap: 4px;\n }\n\n .pp-dev-info__theme-btn {\n padding: 3px 8px;\n border: 1px solid rgba(34, 34, 34, 0.25);\n border-radius: 3px;\n cursor: pointer;\n background: transparent;\n font-size: 11px;\n line-height: 16px;\n color: var(--pp-dev-info-color-secondary);\n transition:\n border-color 0.2s ease,\n color 0.2s ease,\n background-color 0.2s ease;\n\n &:hover {\n border-color: var(--pp-dev-info-color-primary);\n color: var(--pp-dev-info-color-primary);\n }\n\n &.active {\n border-color: var(--pp-dev-info-color-primary);\n background-color: var(--pp-dev-info-color-primary);\n color: var(--pp-dev-info-color-white);\n }\n\n &:focus-visible {\n outline: 2px solid var(--pp-dev-info-color-primary);\n outline-offset: 1px;\n }\n }\n\n .pp-dev-info__settings-hide-btn {\n width: 100%;\n padding: 6px 10px;\n border: 1px solid rgba(34, 34, 34, 0.2);\n border-radius: 4px;\n cursor: pointer;\n font-weight: 600;\n color: var(--pp-dev-info-color-danger);\n transition: background-color 0.2s ease;\n\n &:hover {\n background-color: rgba(172, 43, 43, 0.06);\n }\n }\n\n .pp-dev-info__settings-group {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding-top: 10px;\n border-top: 1px solid rgba(34, 34, 34, 0.12);\n }\n\n .pp-dev-info__vars-buttons {\n display: flex;\n flex-direction: column;\n gap: 6px;\n }\n\n .pp-dev-info__vars-btn {\n width: 100%;\n padding: 6px 10px;\n border-radius: 4px;\n cursor: pointer;\n font-weight: 600;\n text-align: center;\n color: var(--pp-dev-info-color-primary);\n background-color: rgba(7, 91, 126, 0.1);\n border: 1px solid rgba(7, 91, 126, 0.4);\n transition:\n background-color 0.2s ease,\n border-color 0.2s ease;\n\n &:hover {\n background-color: rgba(7, 91, 126, 0.18);\n border-color: rgba(7, 91, 126, 0.6);\n }\n\n &:focus-visible {\n outline: 2px solid var(--pp-dev-info-color-primary);\n outline-offset: 1px;\n }\n }\n\n .pp-dev-info__settings-hint {\n font-size: 10px;\n line-height: 14px;\n color: var(--pp-dev-info-color-secondary);\n\n code {\n font-family: monospace;\n font-size: 10px;\n }\n }\n\n .pp-dev-info__settings-reset {\n align-self: flex-start;\n font-size: 11px;\n color: var(--pp-dev-info-color-primary);\n cursor: pointer;\n\n &:hover {\n text-decoration: underline;\n }\n }\n }\n\n // ── Users (PP-4597) ──────────────────────────────────────────────────────\n &__users-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n padding: 6px;\n border-radius: 3px;\n cursor: pointer;\n color: var(--pp-dev-info-color-secondary);\n transition: all 0.2s ease;\n\n svg {\n width: 16px;\n height: 16px;\n flex-shrink: 0;\n }\n\n &:hover {\n color: var(--pp-dev-info-color-primary);\n background-color: rgba(7, 91, 126, 0.06);\n }\n }\n\n // A saved user's tab: the button stands out, so the tab is never taken for your own.\n &.pp-dev-info--user-tab .pp-dev-info__users-btn {\n color: #ffffff;\n background-color: #7a3fb8;\n\n &:hover {\n background-color: #8d52cc;\n }\n }\n\n &__users.pp-dev-info__settings {\n width: 320px;\n max-width: calc(100vw - 16px);\n box-sizing: border-box;\n\n .pp-dev-info__users-tab {\n font-size: 12px;\n line-height: 16px;\n color: var(--pp-dev-info-color-dark);\n\n span {\n display: block;\n font-size: 10px;\n color: var(--pp-dev-info-color-secondary);\n }\n }\n\n .pp-dev-info__users-list {\n display: flex;\n flex-direction: column;\n gap: 2px;\n max-height: 240px;\n overflow-y: auto;\n }\n\n .pp-dev-info__users-row {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 5px 6px;\n border-radius: 3px;\n border-left: 2px solid transparent;\n\n &.is-current {\n border-left-color: #7a3fb8;\n background-color: rgba(122, 63, 184, 0.08);\n }\n }\n\n .pp-dev-info__users-default {\n appearance: auto;\n -webkit-appearance: radio;\n width: 13px;\n height: 13px;\n margin: 0;\n flex-shrink: 0;\n accent-color: var(--pp-dev-info-color-primary);\n cursor: pointer;\n\n &:disabled {\n cursor: not-allowed;\n }\n }\n\n .pp-dev-info__users-who {\n display: flex;\n flex-direction: column;\n min-width: 0;\n flex: 1;\n }\n\n .pp-dev-info__users-name {\n font-size: 12px;\n font-weight: 600;\n color: var(--pp-dev-info-color-dark);\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .pp-dev-info__users-meta {\n font-size: 10px;\n line-height: 13px;\n color: var(--pp-dev-info-color-secondary);\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .needs-login .pp-dev-info__users-meta {\n color: var(--pp-dev-info-color-danger);\n }\n\n .pp-dev-info__users-action {\n flex-shrink: 0;\n padding: 2px 7px;\n border: 1px solid rgba(34, 34, 34, 0.25);\n border-radius: 3px;\n background: transparent;\n cursor: pointer;\n font-size: 11px;\n line-height: 16px;\n color: var(--pp-dev-info-color-primary);\n\n &:hover {\n border-color: var(--pp-dev-info-color-primary);\n }\n\n &--accent {\n border-color: var(--pp-dev-info-color-primary);\n background-color: var(--pp-dev-info-color-primary);\n color: var(--pp-dev-info-color-white);\n }\n }\n\n .pp-dev-info__users-remove {\n flex-shrink: 0;\n width: 20px;\n height: 20px;\n padding: 0;\n border: none;\n border-radius: 3px;\n background: transparent;\n cursor: pointer;\n font-size: 15px;\n line-height: 20px;\n color: var(--pp-dev-info-color-secondary);\n\n &:hover {\n color: var(--pp-dev-info-color-danger);\n background-color: rgba(172, 43, 43, 0.08);\n }\n }\n\n .pp-dev-info__users-input {\n width: 100%;\n box-sizing: border-box;\n padding: 5px 8px;\n border: 1px solid rgba(34, 34, 34, 0.25);\n border-radius: 3px;\n background: transparent;\n font-size: 12px;\n color: var(--pp-dev-info-color-dark);\n\n &:focus {\n outline: none;\n border-color: var(--pp-dev-info-color-primary);\n }\n }\n\n .pp-dev-info__users-error,\n .pp-dev-info__users-unavailable {\n font-size: 11px;\n line-height: 15px;\n color: var(--pp-dev-info-color-danger);\n }\n\n .pp-dev-info__users-unavailable {\n color: var(--pp-dev-info-color-secondary);\n }\n\n [hidden] {\n display: none !important;\n }\n }\n\n &__wrap-btn {\n box-sizing: border-box;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n padding: 6px;\n cursor: pointer;\n border-radius: 3px;\n transition: background-color 0.2s ease;\n\n &:hover {\n background-color: rgba(7, 91, 126, 0.06);\n animation: arrowHover 0.8s linear infinite;\n }\n\n &-arrow {\n width: 16px;\n height: 16px;\n color: var(--pp-dev-info-color-dark);\n fill: none;\n transition: transform 0.2s ease;\n }\n }\n\n &.closed {\n z-index: 100002;\n\n & .pp-dev-info__wrap-btn {\n &:hover {\n animation: arrowHover 1.2s linear infinite;\n }\n }\n }\n\n // Corner variants: placement, center-facing radius/shadow, direction-aware\n // minimize (.closed) and auto-hide slides. The corner class is always rendered\n // on the panel root (server default: bottom-right).\n @each $name, $c in $pp-dev-corners {\n &.pp-dev-info--#{$name} {\n #{map.get($c, v)}: 0;\n #{map.get($c, h)}: 0;\n border-radius: map.get($c, radius);\n box-shadow: map.get($c, shadow);\n #{map.get($c, edge)}: 1px solid rgba(34, 34, 34, 0.08);\n\n .pp-dev-info__settings {\n #{map.get($c, v)}: calc(100% + 8px);\n #{map.get($c, h)}: 0;\n }\n\n @if map.get($c, sx) == 1 {\n &.closed {\n transform: translateX(calc(100% - $pp-dev-height));\n\n .pp-dev-info__wrap-btn-arrow {\n transform: rotate(180deg);\n }\n }\n\n &.pp-dev-info--auto-hide:not(.is-peeking):not(.is-dragging) {\n transform: translateX(calc(100% - $pp-dev-peek));\n box-shadow:\n map.get($c, shadow),\n inset $pp-dev-peek 0 0 0 var(--pp-dev-info-color-primary);\n\n // Extended invisible hover zone on the inner side of the 4px strip\n &::after {\n content: '';\n position: absolute;\n top: -6px;\n bottom: -6px;\n left: -8px;\n width: 8px;\n }\n }\n } @else {\n .pp-dev-info__wrap-btn {\n order: 99;\n\n &-arrow {\n transform: rotate(180deg);\n }\n }\n\n &.closed {\n transform: translateX(calc(-100% + $pp-dev-height));\n\n .pp-dev-info__wrap-btn-arrow {\n transform: rotate(0deg);\n }\n }\n\n &.pp-dev-info--auto-hide:not(.is-peeking):not(.is-dragging) {\n transform: translateX(calc(-100% + $pp-dev-peek));\n box-shadow:\n map.get($c, shadow),\n inset (-$pp-dev-peek) 0 0 0 var(--pp-dev-info-color-primary);\n\n &::after {\n content: '';\n position: absolute;\n top: -6px;\n bottom: -6px;\n right: -8px;\n width: 8px;\n }\n }\n }\n }\n }\n\n &.pp-dev-info--auto-hide {\n z-index: 100002;\n }\n\n &.is-dragging {\n transition: none;\n user-select: none;\n cursor: grabbing;\n\n .pp-dev-info__drag-handle {\n cursor: grabbing;\n }\n }\n\n &.pp-dev-info--hidden {\n display: none;\n }\n\n .sync-button {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n padding: 6px;\n background-color: transparent;\n border-radius: 3px;\n cursor: pointer;\n color: var(--pp-dev-info-color-primary);\n transition: all 0.2s ease;\n position: relative;\n overflow: hidden;\n\n &::before {\n content: '';\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background-color: currentColor;\n opacity: 0;\n transition: opacity 0.2s ease;\n border-radius: 3px;\n }\n\n svg {\n width: 16px;\n height: 16px;\n position: relative;\n z-index: 1;\n transition: transform 0.3s ease;\n }\n\n &:hover:not(.disabled) {\n color: var(--pp-dev-info-color-primary);\n background-color: rgba(7, 91, 126, 0.06);\n\n svg {\n transform: scale(1.1) rotate(45deg);\n transition: transform 0.2s ease;\n }\n }\n\n &:active:not(.disabled) {\n svg {\n transform: scale(0.9) rotate(45deg);\n }\n }\n\n &.disabled {\n opacity: 0.5;\n cursor: not-allowed;\n pointer-events: none;\n color: var(--pp-dev-info-color-secondary);\n }\n\n &.syncing {\n svg {\n animation: rotate 1.5s linear infinite;\n }\n }\n }\n\n &__sep {\n display: inline-flex;\n flex-shrink: 0;\n width: 1px;\n height: 16px;\n background-color: rgba(34, 34, 34, 0.24);\n\n html[data-pp-dev-theme='dark'] & {\n background-color: rgba(255, 255, 255, 0.2);\n }\n\n @media screen and (prefers-color-scheme: dark) {\n background-color: rgba(255, 255, 255, 0.2);\n\n html[data-pp-dev-theme='light'] & {\n background-color: rgba(34, 34, 34, 0.24);\n }\n }\n }\n\n @keyframes rotate {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n }\n\n .margin-5 {\n margin-right: 5px;\n }\n\n .margin-10 {\n margin-right: 10px;\n }\n\n .custom-font {\n font-family: 'Inter', sans-serif;\n font-size: 13px;\n font-weight: 400;\n vertical-align: middle;\n line-height: 18px;\n color: var(--pp-dev-info-color-dark);\n\n &:not(.not-colored) a {\n color: var(--pp-dev-info-color-primary) !important;\n }\n }\n\n @keyframes rotation {\n 0% {\n transform: rotate(0deg);\n }\n 100% {\n transform: rotate(360deg);\n }\n }\n\n @keyframes arrowHover {\n 0% {\n transform: translateX(2px);\n }\n 50% {\n transform: translateX(-2px);\n }\n 100% {\n transform: translateX(2px);\n }\n }\n\n // Explicit Dark pick (html[data-pp-dev-theme='dark'], set via the settings popover's Theme\n // row — see src/client/theme.ts) — always wins over both the light default above and the\n // Auto media query below, on selector specificity alone.\n html[data-pp-dev-theme='dark'] & {\n @include pp-dev-theme-colors($pp-dev-dark-colors);\n @include pp-dev-dark-tints;\n }\n\n // Auto (OS/browser preference) — same values, only takes effect when no explicit\n // html[data-pp-dev-theme] override is set (those always outrank a plain :root/& selector).\n @media screen and (prefers-color-scheme: dark) {\n @include pp-dev-theme-colors($pp-dev-dark-colors);\n @include pp-dev-dark-tints;\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n &.pp-dev-info,\n .pp-dev-info,\n .pp-dev-info__popup,\n .pp-dev-info__settings {\n transition: none;\n }\n }\n}\n"]}
@@ -644,6 +644,360 @@ function initPanelSettings($panel, controller, hooks) {
644
644
  });
645
645
  }
646
646
 
647
+ /**
648
+ * The dev panel's "Users" popover (PP-4597): the user of this tab, the saved users (open as, log
649
+ * in, make default, remove) and adding a user by API token or by username + password. Each user
650
+ * works on its own host, `<name>.localhost:<port>`; see src/lib/user-registry.ts.
651
+ */
652
+ const USERS_API = '/@api/users';
653
+ const REFRESH_INTERVAL = 3000;
654
+ const USER_HOST_REGEXP = /^([a-z](?:[a-z0-9-]*[a-z0-9])?)\.localhost$/;
655
+ const STATE_LABELS = {
656
+ pending: 'not logged in yet',
657
+ active: 'logged in',
658
+ expired: 'session expired',
659
+ };
660
+ /** The saved user this tab belongs to, from its host; `undefined` on the main host. */
661
+ function getTabUserName(hostname = window.location.hostname) {
662
+ return USER_HOST_REGEXP.exec(hostname)?.[1];
663
+ }
664
+ /** Users need `*.localhost` hosts, which only exist when the dev server is opened on localhost. */
665
+ function isOnLocalhost(hostname = window.location.hostname) {
666
+ return hostname === 'localhost' || hostname.endsWith('.localhost');
667
+ }
668
+ function mainOrigin() {
669
+ const { protocol, port } = window.location;
670
+ return `${protocol}//localhost${port ? `:${port}` : ''}`;
671
+ }
672
+ /** The current page on another origin, so "open as" keeps the page you are on. */
673
+ function samePageOn(origin) {
674
+ const { pathname, search, hash } = window.location;
675
+ return `${origin}${pathname}${search}${hash}`;
676
+ }
677
+ function escapeHtml(value) {
678
+ return value.replace(/[&<>"']/g, (ch) => `&#${ch.charCodeAt(0)};`);
679
+ }
680
+ function describeUser(user) {
681
+ const who = user.miUser?.username || (user.miUser ? `MI user ${user.miUser.id}` : '');
682
+ const how = user.kind === 'token'
683
+ ? user.state === 'expired'
684
+ ? 'API token turned down by MI'
685
+ : 'API token'
686
+ : STATE_LABELS[user.state ?? 'pending'];
687
+ return [who, how].filter(Boolean).join(' · ');
688
+ }
689
+ async function readError(response) {
690
+ try {
691
+ const body = await response.json();
692
+ return body?.error || `Request failed (${response.status})`;
693
+ }
694
+ catch {
695
+ return `Request failed (${response.status})`;
696
+ }
697
+ }
698
+ function initUsersPanel($panel, hooks) {
699
+ const $btn = $panel.querySelector('.pp-dev-info__users-btn');
700
+ if (!$btn) {
701
+ return;
702
+ }
703
+ const tabUser = getTabUserName();
704
+ // On a saved user's host the button stands out (the name is in its tooltip and the popover, so a
705
+ // long name doesn't widen the panel), so the tab is never mistaken for your own.
706
+ if (tabUser) {
707
+ $panel.classList.add('pp-dev-info--user-tab');
708
+ $btn.title = `This tab works as ${tabUser}. Users…`;
709
+ $btn.setAttribute('aria-label', `Users (this tab works as ${tabUser})`);
710
+ }
711
+ let $popover = null;
712
+ let refreshTimer;
713
+ let addKind = 'token';
714
+ let data = null;
715
+ // What the list shows now, so a refresh with the same data leaves it (and the focus) alone.
716
+ let renderedData = '';
717
+ const onDocClick = (ev) => {
718
+ const target = ev.target;
719
+ if ($popover && !$popover.contains(target) && !$btn.contains(target)) {
720
+ close();
721
+ }
722
+ };
723
+ const onKeyDown = (ev) => {
724
+ if (ev.key === 'Escape') {
725
+ close();
726
+ }
727
+ };
728
+ const close = () => {
729
+ if (!$popover) {
730
+ return;
731
+ }
732
+ window.clearInterval(refreshTimer);
733
+ $popover.remove();
734
+ $popover = null;
735
+ document.removeEventListener('click', onDocClick, true);
736
+ document.removeEventListener('keydown', onKeyDown, true);
737
+ hooks?.onOpenChange?.(false);
738
+ };
739
+ const setError = (message) => {
740
+ const $error = $popover?.querySelector('.pp-dev-info__users-error');
741
+ if ($error) {
742
+ $error.textContent = message;
743
+ $error.hidden = !message;
744
+ }
745
+ };
746
+ const request = async (method, path, body) => {
747
+ try {
748
+ const response = await fetch(path, {
749
+ method,
750
+ headers: body === undefined ? undefined : { 'Content-Type': 'application/json' },
751
+ body: body === undefined ? undefined : JSON.stringify(body),
752
+ });
753
+ if (!response.ok) {
754
+ setError(await readError(response));
755
+ return null;
756
+ }
757
+ setError('');
758
+ return response;
759
+ }
760
+ catch {
761
+ setError('pp-dev is not reachable');
762
+ return null;
763
+ }
764
+ };
765
+ const renderList = () => {
766
+ const $list = $popover?.querySelector('.pp-dev-info__users-list');
767
+ if (!$popover || !$list || !data) {
768
+ return;
769
+ }
770
+ const snapshot = JSON.stringify(data);
771
+ if (snapshot === renderedData) {
772
+ return;
773
+ }
774
+ renderedData = snapshot;
775
+ // The control that has the keyboard focus, to focus its counterpart in the new list.
776
+ const focusKey = document.activeElement instanceof HTMLElement && $list.contains(document.activeElement)
777
+ ? document.activeElement.dataset.focusKey
778
+ : undefined;
779
+ const defaultUser = data.defaultUser;
780
+ // The main host (localhost) works as the default user, a user's host as that user, and an IP
781
+ // address or a custom domain as you.
782
+ const currentUser = tabUser ?? (window.location.hostname === 'localhost' ? defaultUser : null) ?? undefined;
783
+ const $tab = $popover.querySelector('.pp-dev-info__users-tab strong');
784
+ if ($tab) {
785
+ $tab.textContent = currentUser ?? 'you';
786
+ }
787
+ const rows = [
788
+ `<div class="pp-dev-info__users-row${currentUser ? '' : ' is-current'}">
789
+ <input type="radio" name="pp-dev-default-user" class="pp-dev-info__users-default" value="" data-focus-key=":default"
790
+ title="Make the main host (localhost) work as you" ${defaultUser ? '' : 'checked'} />
791
+ <div class="pp-dev-info__users-who">
792
+ <span class="pp-dev-info__users-name">You</span>
793
+ <span class="pp-dev-info__users-meta">original user · the dev panel always works as you</span>
794
+ </div>
795
+ ${
796
+ // The main host works as you only while no other user is the default; then that user's
797
+ // row is the one to open.
798
+ tabUser && !defaultUser
799
+ ? `<button type="button" class="pp-dev-info__users-action" data-open="${escapeHtml(mainOrigin())}" data-focus-key=":open" title="Open this page on the main host">Open</button>`
800
+ : ''}
801
+ </div>`,
802
+ ...data.users.map((user) => {
803
+ const canBeDefault = user.kind === 'token' ? user.state !== 'expired' : user.state === 'active';
804
+ const needsLogin = user.kind === 'session' && user.state !== 'active';
805
+ const needsAttention = needsLogin || user.state === 'expired';
806
+ const name = escapeHtml(user.name);
807
+ let defaultTitle = `Make the main host (localhost) work as ${name}`;
808
+ if (!canBeDefault) {
809
+ defaultTitle =
810
+ user.kind === 'token' ? `MI turned down the API token of ${name}` : `${name} has to log in first`;
811
+ }
812
+ return `<div class="pp-dev-info__users-row${user.name === currentUser ? ' is-current' : ''}${needsAttention ? ' needs-login' : ''}" data-user="${name}">
813
+ <input type="radio" name="pp-dev-default-user" class="pp-dev-info__users-default" value="${name}" data-focus-key="${name}:default"
814
+ title="${defaultTitle}"
815
+ ${user.name === defaultUser ? 'checked' : ''} ${canBeDefault ? '' : 'disabled'} />
816
+ <div class="pp-dev-info__users-who">
817
+ <span class="pp-dev-info__users-name">${name}</span>
818
+ <span class="pp-dev-info__users-meta">${escapeHtml(describeUser(user))}</span>
819
+ </div>
820
+ ${needsLogin && user.loginUrl
821
+ ? `<button type="button" class="pp-dev-info__users-action pp-dev-info__users-action--accent" data-open="${escapeHtml(user.loginUrl)}" data-raw="true" data-focus-key="${name}:login">Log in</button>`
822
+ : ''}
823
+ <button type="button" class="pp-dev-info__users-action" data-open="${escapeHtml(user.origin)}" data-focus-key="${name}:open" title="Open this page as ${name}">Open</button>
824
+ <button type="button" class="pp-dev-info__users-remove" data-remove="${name}" data-focus-key="${name}:remove" title="Remove ${name}" aria-label="Remove ${name}">×</button>
825
+ </div>`;
826
+ }),
827
+ ];
828
+ $list.innerHTML = rows.join('');
829
+ if (focusKey !== undefined) {
830
+ // The same control of the same user, or the list's first control if it is gone.
831
+ const $same = [...$list.querySelectorAll('[data-focus-key]')].find(($el) => $el.dataset.focusKey === focusKey);
832
+ ($same ?? $list.querySelector('[data-focus-key]'))?.focus();
833
+ }
834
+ $list.querySelectorAll('[data-open]').forEach(($open) => {
835
+ $open.addEventListener('click', (ev) => {
836
+ ev.preventDefault();
837
+ const target = $open.dataset.open;
838
+ window.open($open.dataset.raw ? target : samePageOn(target), '_blank');
839
+ });
840
+ });
841
+ $list.querySelectorAll('[data-remove]').forEach(($remove) => {
842
+ $remove.addEventListener('click', async (ev) => {
843
+ ev.preventDefault();
844
+ if (await request('DELETE', `${USERS_API}/${encodeURIComponent($remove.dataset.remove)}`)) {
845
+ await refresh();
846
+ }
847
+ });
848
+ });
849
+ $list.querySelectorAll('.pp-dev-info__users-default').forEach(($radio) => {
850
+ $radio.addEventListener('change', async () => {
851
+ await request('PUT', `${USERS_API}/default`, { name: $radio.value || null });
852
+ // Redrawn even if refused: the radio the click checked must go back.
853
+ renderedData = '';
854
+ await refresh();
855
+ });
856
+ });
857
+ };
858
+ const renderAvailability = () => {
859
+ if (!$popover || !data) {
860
+ return;
861
+ }
862
+ const $form = $popover.querySelector('.pp-dev-info__users-add');
863
+ const $unavailable = $popover.querySelector('.pp-dev-info__users-unavailable');
864
+ let reason = '';
865
+ if (!isOnLocalhost()) {
866
+ reason = 'Users open on <name>.localhost hosts: open the dev server on localhost to use them.';
867
+ }
868
+ else if (!data.available) {
869
+ reason = 'Adding users needs your own API token: set mi.token or MI_ACCESS_TOKEN.';
870
+ }
871
+ if ($form) {
872
+ $form.hidden = Boolean(reason);
873
+ }
874
+ if ($unavailable) {
875
+ $unavailable.textContent = reason;
876
+ $unavailable.hidden = !reason;
877
+ }
878
+ };
879
+ const refresh = async () => {
880
+ try {
881
+ const response = await fetch(USERS_API);
882
+ if (!response.ok) {
883
+ return;
884
+ }
885
+ data = (await response.json());
886
+ renderList();
887
+ renderAvailability();
888
+ }
889
+ catch {
890
+ // The next refresh tries again.
891
+ }
892
+ };
893
+ const renderKind = () => {
894
+ if (!$popover) {
895
+ return;
896
+ }
897
+ $popover.querySelectorAll('.pp-dev-info__users-kind').forEach(($kind) => {
898
+ const active = $kind.dataset.kind === addKind;
899
+ $kind.classList.toggle('active', active);
900
+ $kind.setAttribute('aria-pressed', String(active));
901
+ });
902
+ const $token = $popover.querySelector('.pp-dev-info__users-token');
903
+ const $submit = $popover.querySelector('.pp-dev-info__users-submit');
904
+ const $kindHint = $popover.querySelector('.pp-dev-info__users-kind-hint');
905
+ if ($token) {
906
+ $token.hidden = addKind !== 'token';
907
+ }
908
+ if ($submit) {
909
+ $submit.textContent = addKind === 'token' ? 'Add user' : 'Add and log in…';
910
+ }
911
+ if ($kindHint) {
912
+ $kindHint.textContent =
913
+ addKind === 'token'
914
+ ? 'Checked with MI before the user is added. SSO users can only be added this way.'
915
+ : "Opens MI's login page on the user's own host. pp-dev never sees the password.";
916
+ }
917
+ };
918
+ const submit = async () => {
919
+ if (!$popover) {
920
+ return;
921
+ }
922
+ const $name = $popover.querySelector('.pp-dev-info__users-name-input');
923
+ const $token = $popover.querySelector('.pp-dev-info__users-token');
924
+ const $submit = $popover.querySelector('.pp-dev-info__users-submit');
925
+ const body = addKind === 'token'
926
+ ? { name: $name.value.trim(), token: $token.value }
927
+ : { name: $name.value.trim(), kind: 'session' };
928
+ $submit.disabled = true;
929
+ const response = await request('POST', USERS_API, body);
930
+ $submit.disabled = false;
931
+ if (!response) {
932
+ return;
933
+ }
934
+ const { user } = (await response.json());
935
+ $name.value = '';
936
+ $token.value = '';
937
+ if (user.kind === 'session' && user.loginUrl) {
938
+ window.open(user.loginUrl, '_blank');
939
+ }
940
+ await refresh();
941
+ };
942
+ const open = () => {
943
+ renderedData = '';
944
+ $popover = document.createElement('div');
945
+ $popover.classList.add('pp-dev-info__settings', 'pp-dev-info__users');
946
+ $popover.innerHTML = `
947
+ <div class="pp-dev-info__settings-title">Users</div>
948
+ <div class="pp-dev-info__users-tab">
949
+ This tab works as <strong>${escapeHtml(tabUser ?? 'you')}</strong>.
950
+ <span>localhost works as the checked user; each user's own host as that user.</span>
951
+ </div>
952
+ <div class="pp-dev-info__users-list" role="radiogroup" aria-label="Default user"></div>
953
+ <div class="pp-dev-info__users-unavailable" hidden></div>
954
+ <form class="pp-dev-info__settings-group pp-dev-info__users-add" hidden>
955
+ <span class="pp-dev-info__settings-label">Add user</span>
956
+ <div class="pp-dev-info__theme-grid" role="group" aria-label="How the user logs in">
957
+ <button type="button" class="pp-dev-info__theme-btn pp-dev-info__users-kind" data-kind="token">API token</button>
958
+ <button type="button" class="pp-dev-info__theme-btn pp-dev-info__users-kind" data-kind="session">Username + password</button>
959
+ </div>
960
+ <input class="pp-dev-info__users-input pp-dev-info__users-name-input" placeholder="Name, e.g. viewer" maxlength="32"
961
+ autocomplete="off" spellcheck="false" pattern="[a-z]([a-z0-9-]*[a-z0-9])?"
962
+ title="Lowercase latin letters, digits and -, starting with a letter. The user opens on <name>.localhost." />
963
+ <input class="pp-dev-info__users-input pp-dev-info__users-token" type="password" placeholder="API token" autocomplete="off" />
964
+ <button type="submit" class="pp-dev-info__vars-btn pp-dev-info__users-submit">Add user</button>
965
+ <div class="pp-dev-info__settings-hint pp-dev-info__users-kind-hint"></div>
966
+ </form>
967
+ <div class="pp-dev-info__users-error" role="alert" hidden></div>
968
+ <div class="pp-dev-info__settings-hint">Each user opens on <code>&lt;name&gt;.localhost</code> and is kept until the dev server stops.</div>
969
+ `;
970
+ $popover.querySelectorAll('.pp-dev-info__users-kind').forEach(($kind) => {
971
+ $kind.addEventListener('click', (ev) => {
972
+ ev.preventDefault();
973
+ addKind = $kind.dataset.kind;
974
+ renderKind();
975
+ });
976
+ });
977
+ $popover.querySelector('.pp-dev-info__users-add').addEventListener('submit', (ev) => {
978
+ ev.preventDefault();
979
+ submit();
980
+ });
981
+ renderKind();
982
+ $panel.appendChild($popover);
983
+ document.addEventListener('click', onDocClick, true);
984
+ document.addEventListener('keydown', onKeyDown, true);
985
+ hooks?.onOpenChange?.(true);
986
+ refresh();
987
+ // Picks up logins and expired sessions while the popover is open.
988
+ refreshTimer = window.setInterval(refresh, REFRESH_INTERVAL);
989
+ };
990
+ $btn.addEventListener('click', (ev) => {
991
+ ev.preventDefault();
992
+ if ($popover) {
993
+ close();
994
+ }
995
+ else {
996
+ open();
997
+ }
998
+ });
999
+ }
1000
+
647
1001
  const ICON_SIZE = 16;
648
1002
  const CLOSE_ICON_SIZE = 12;
649
1003
  const TYPE_ICONS = {
@@ -895,6 +1249,7 @@ if ($infoPanel) {
895
1249
  onOpenInspectorClick: () => window.open(window.location.origin + '/@pp-dev/inspector', '_blank'),
896
1250
  onReloadVariablesClick: () => startReloadVariablesFlow?.(),
897
1251
  });
1252
+ initUsersPanel($infoPanel, { onOpenChange: (open) => autoHide.keepPeeked(open) });
898
1253
  panelController.onChange((state) => {
899
1254
  if (!state.autoHide) {
900
1255
  $infoPanel.classList.remove('is-peeking');