@hienlh/ppm 0.22.0 → 0.22.2

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 (147) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/CLAUDE.md +1 -1
  3. package/assets/skills/ppm/SKILL.md +1 -1
  4. package/assets/skills/ppm/references/cli-reference.md +4 -4
  5. package/assets/skills/ppm/references/http-api.md +1 -1
  6. package/dist/web/assets/accounts-settings-section-CLEAA_Jc.js +1 -0
  7. package/dist/web/assets/{ai-resource-editor-CdDpOwjh.js → ai-resource-editor-C4ecid2g.js} +1 -1
  8. package/dist/web/assets/ai-settings-section-BOwGdiyH.js +1 -0
  9. package/dist/web/assets/ai-settings-section-Cqz13gRt.js +1 -0
  10. package/dist/web/assets/{api-client-C1ZwRL4Y.js → api-client-Jn3njtNw.js} +1 -1
  11. package/dist/web/assets/api-client-rSFnnCj4.js +1 -0
  12. package/dist/web/assets/{api-settings-_7Q-ULxp.js → api-settings-BxijvPBR.js} +1 -1
  13. package/dist/web/assets/{api-themes-DRAERo4Q.js → api-themes-CSUarkzD.js} +1 -1
  14. package/dist/web/assets/{appearance-settings-section-DLoPK0LD.js → appearance-settings-section-DbA8L-Vz.js} +1 -1
  15. package/dist/web/assets/{audio-preview-DsYvgKqj.js → audio-preview-CxJd8wRV.js} +1 -1
  16. package/dist/web/assets/{branch-review-tab-G84Ojzae.js → branch-review-tab-CMTa2uSR.js} +1 -1
  17. package/dist/web/assets/chat-tab-BpdgqjSL.js +15 -0
  18. package/dist/web/assets/{code-editor-D04iUPsZ.js → code-editor-B7y7-5KE.js} +4 -4
  19. package/dist/web/assets/{codex-daily-guard-BSccLRGT.js → codex-daily-guard-ChDPiXDx.js} +1 -1
  20. package/dist/web/assets/{conflict-editor-C4ay676e.js → conflict-editor-Axs9Ad7A.js} +3 -3
  21. package/dist/web/assets/{csv-preview-DbwEmG7o.js → csv-preview-DTUAW2HW.js} +1 -1
  22. package/dist/web/assets/{database-viewer-S6LsALjX.js → database-viewer-qXDiWeXj.js} +1 -1
  23. package/dist/web/assets/{diff-viewer-3oBA7vgQ.js → diff-viewer-BtLiSpqe.js} +1 -1
  24. package/dist/web/assets/{docx-preview-CXuk-emm.js → docx-preview-Dxvy6h13.js} +1 -1
  25. package/dist/web/assets/{editor-language-service-DJmaKVY0.js → editor-language-service-D75GuLXP.js} +1 -1
  26. package/dist/web/assets/{explorer-body-BQ0jyAGv.js → explorer-body-xg1-MhlW.js} +2 -2
  27. package/dist/web/assets/{explorer-window-content-jdkWt34J.js → explorer-window-content-BeQocW3i.js} +1 -1
  28. package/dist/web/assets/extension-manager-section-Bm2v3P-n.js +1 -0
  29. package/dist/web/assets/{extension-webview-BGSDUbdK.js → extension-webview-CzxrAHyL.js} +1 -1
  30. package/dist/web/assets/{file-icons-BXdvggK2.js → file-icons-wS-OmEVu.js} +1 -1
  31. package/dist/web/assets/{file-store-ojgMXgAw.js → file-store-Ds3qJ4hC.js} +1 -1
  32. package/dist/web/assets/{files-settings-section-Gnyd7han.js → files-settings-section-Bd-_GeZU.js} +1 -1
  33. package/dist/web/assets/{format-file-meta-C77h04cZ.js → format-file-meta-PPi3ptkm.js} +1 -1
  34. package/dist/web/assets/{general-settings-section-V6Khh15V.js → general-settings-section-CfNPHQUk.js} +1 -1
  35. package/dist/web/assets/{git-log-panel-Bi513qjT.js → git-log-panel-CtGxPzH6.js} +1 -1
  36. package/dist/web/assets/{git-repo-store-88Z8v-pF.js → git-repo-store-BCEPfUGT.js} +1 -1
  37. package/dist/web/assets/{glide-data-grid-DnQwYSmQ.js → glide-data-grid-iSo2vP0q.js} +1 -1
  38. package/dist/web/assets/{group-chat-tab-DpczdwyV.js → group-chat-tab-D1lx0zz-.js} +1 -1
  39. package/dist/web/assets/{image-preview-CzCc4_Do.js → image-preview-DeGD0HJu.js} +1 -1
  40. package/dist/web/assets/index-CN0G8uYY.js +52 -0
  41. package/dist/web/assets/{jira-watcher-section-f2HUbGDY.js → jira-watcher-section-kyifZPTv.js} +1 -1
  42. package/dist/web/assets/keybindings-store-L5vV8DEJ.js +1 -0
  43. package/dist/web/assets/{keyboard-shortcuts-section-BpvVEw_Y.js → keyboard-shortcuts-section-D2uJq4x9.js} +1 -1
  44. package/dist/web/assets/markdown-renderer-BX1Q00sC.js +3 -0
  45. package/dist/web/assets/markdown-renderer-CLUux0vA.js +1 -0
  46. package/dist/web/assets/{media-url-yfqHpPz0.js → media-url-DKK0YHxL.js} +1 -1
  47. package/dist/web/assets/{mobile-explorer-sheet-Bol3XAO1.js → mobile-explorer-sheet-BHQDzpwK.js} +1 -1
  48. package/dist/web/assets/notification-store-BHgSy-ci.js +1 -0
  49. package/dist/web/assets/panel-store-D20Zuf3i.js +1 -0
  50. package/dist/web/assets/{pdf-preview-CmBI6E7d.js → pdf-preview-DAcfhfGm.js} +1 -1
  51. package/dist/web/assets/{postgres-viewer-8ZAqEALv.js → postgres-viewer-DPRsmgP5.js} +1 -1
  52. package/dist/web/assets/{ppmbot-settings-section-DKJUA3Kq.js → ppmbot-settings-section-B5N1jHgm.js} +1 -1
  53. package/dist/web/assets/{problems-panel-CFcoUx8h.js → problems-panel-D_wZ4_Qg.js} +1 -1
  54. package/dist/web/assets/{process-table-EKr2dqG6.js → process-table-DahflyQn.js} +1 -1
  55. package/dist/web/assets/{project-framework-store-B70Q3dNM.js → project-framework-store-ViZgce6y.js} +1 -1
  56. package/dist/web/assets/{project-store-B87hLRSk.js → project-store-hNvwfqX3.js} +1 -1
  57. package/dist/web/assets/{proxy-settings-section-B7qRVl8w.js → proxy-settings-section-CLg_nlxn.js} +1 -1
  58. package/dist/web/assets/{query-audit-section-By6qyqdB.js → query-audit-section-BrfSo0s_.js} +1 -1
  59. package/dist/web/assets/{remote-desktop-clipboard-notice-0WDYsyCA.js → remote-desktop-clipboard-notice-Fi6cieGM.js} +1 -1
  60. package/dist/web/assets/{remote-desktop-mobile-sheet-COYDg16H.js → remote-desktop-mobile-sheet-lLZ-oK73.js} +2 -2
  61. package/dist/web/assets/{remote-desktop-mobile-view-2DqSuS2U.js → remote-desktop-mobile-view-j3v-4rYa.js} +1 -1
  62. package/dist/web/assets/{remote-desktop-readiness-gate-Dm_qMJMH.js → remote-desktop-readiness-gate-CeLvcX_w.js} +1 -1
  63. package/dist/web/assets/{remote-desktop-window-content-UodhsF7W.js → remote-desktop-window-content-lwbkLsTC.js} +1 -1
  64. package/dist/web/assets/{run-in-terminal-CdefEe_R.js → run-in-terminal-D7W4IKb9.js} +1 -1
  65. package/dist/web/assets/{schedules-settings-section-bG2lYfGv.js → schedules-settings-section-C8OS0o80.js} +1 -1
  66. package/dist/web/assets/{send-to-chat-F207yH3u.js → send-to-chat-DNKhmHbi.js} +1 -1
  67. package/dist/web/assets/{settings-body-C06bFarv.js → settings-body-DgN22Z0W.js} +2 -2
  68. package/dist/web/assets/{settings-store-krC6bc79.js → settings-store-CsVSVgLp.js} +2 -2
  69. package/dist/web/assets/{settings-tab-34RSaASd.js → settings-tab-ChOs94iB.js} +1 -1
  70. package/dist/web/assets/{settings-window-content-Dr_2lmHH.js → settings-window-content-BnKMmg2a.js} +1 -1
  71. package/dist/web/assets/{sql-query-editor-BgRH0cHD.js → sql-query-editor-BJZIIQsm.js} +1 -1
  72. package/dist/web/assets/{sqlite-viewer-DUxGslpa.js → sqlite-viewer--cMeCZum.js} +1 -1
  73. package/dist/web/assets/{system-monitor-body-IZuLn7Qh.js → system-monitor-body-CXiIrn0N.js} +2 -2
  74. package/dist/web/assets/system-monitor-tab-BJKrTSl0.js +1 -0
  75. package/dist/web/assets/{system-monitor-window-content-CEaFISgj.js → system-monitor-window-content-Dy9fZJE3.js} +1 -1
  76. package/dist/web/assets/{tab-host-window-content-D0TN8Wrq.js → tab-host-window-content-BezqrAwC.js} +1 -1
  77. package/dist/web/assets/{tab-store-OJa1GPCq.js → tab-store-krAuHQ6_.js} +1 -1
  78. package/dist/web/assets/{team-member-sheet-DDPNcCjd.js → team-member-sheet-CVM2QC3H.js} +2 -2
  79. package/dist/web/assets/{team-member-window-content-DCiW_1-E.js → team-member-window-content-D4RYKplm.js} +1 -1
  80. package/dist/web/assets/{terminal-tab-4ypP0NDY.js → terminal-tab-jikhQLct.js} +3 -3
  81. package/dist/web/assets/{tool-cards-Dd6E7Q3k.js → tool-cards-D5jCQvqn.js} +2 -2
  82. package/dist/web/assets/{use-blob-url-CPtCcY3i.js → use-blob-url-DMGlPCQD.js} +1 -1
  83. package/dist/web/assets/{use-inline-blame-vrfFHuaR.js → use-inline-blame-BF8Y1KfV.js} +3 -3
  84. package/dist/web/assets/{use-lsp-CsuZu49X.js → use-lsp-Bx4oVtlk.js} +1 -1
  85. package/dist/web/assets/{use-monaco-theme-BIVYJBIn.js → use-monaco-theme-DHczXLMb.js} +1 -1
  86. package/dist/web/assets/{use-websocket-BMbufhX7.js → use-websocket-BWZkbleJ.js} +1 -1
  87. package/dist/web/assets/{video-preview-Byj8P_jA.js → video-preview-D191TvX9.js} +1 -1
  88. package/dist/web/assets/{window-pip-registry-yD6esSju.js → window-pip-registry-D1oXq-Dq.js} +1 -1
  89. package/dist/web/assets/ws-auth-BG-cdrQ8.js +1 -0
  90. package/dist/web/index.html +13 -13
  91. package/dist/web/sw.js +1 -1
  92. package/docs/architecture/ai-chat-and-providers.md +9 -0
  93. package/docs/architecture/workspace-and-ui.md +13 -0
  94. package/package.json +1 -1
  95. package/packages/ext-git-graph/src/webview-html.test.ts +30 -5
  96. package/packages/ext-git-graph/src/webview-html.ts +99 -31
  97. package/src/cli/commands/init.ts +1 -0
  98. package/src/providers/codex-app-server/codex-history.ts +26 -7
  99. package/src/providers/codex-app-server/codex-provider.ts +6 -4
  100. package/src/server/routes/chat.ts +1 -1
  101. package/src/server/routes/settings.ts +11 -5
  102. package/src/services/config.service.ts +2 -0
  103. package/src/services/edge-forwarder.ts +191 -191
  104. package/src/services/windows-process-tree.ts +351 -351
  105. package/src/shared/codex-daily-guard.ts +12 -5
  106. package/src/types/chat.ts +1 -1
  107. package/src/types/config.ts +12 -1
  108. package/src/web/components/chat/chat-tab.tsx +162 -38
  109. package/src/web/components/chat/message-input.tsx +24 -0
  110. package/src/web/components/chat/model-thinking-selector.tsx +25 -5
  111. package/src/web/components/chat/new-chat-provider-gate.tsx +68 -0
  112. package/src/web/components/extensions/extension-tree-view.tsx +4 -11
  113. package/src/web/components/layout/command-palette.tsx +2 -18
  114. package/src/web/components/layout/editor-panel.tsx +1 -1
  115. package/src/web/components/layout/reparenting-tab.tsx +4 -2
  116. package/src/web/components/layout/tab-bar.tsx +1 -1
  117. package/src/web/components/settings/accounts/account-card.tsx +2 -2
  118. package/src/web/components/settings/accounts/codex-accounts-section.tsx +4 -4
  119. package/src/web/components/settings/ai-settings-section.tsx +36 -0
  120. package/src/web/components/settings/extension-manager-section.tsx +8 -1
  121. package/src/web/hooks/use-draft.ts +25 -4
  122. package/src/web/hooks/use-usage.ts +6 -4
  123. package/src/web/lib/api-client.ts +7 -1
  124. package/src/web/lib/api-settings.ts +1 -0
  125. package/src/web/lib/extension-icons.ts +70 -0
  126. package/src/web/lib/new-chat-provider.ts +8 -0
  127. package/src/web/lib/tab-type-icons.ts +14 -0
  128. package/src/web/stores/panel-store.ts +46 -3
  129. package/dist/web/assets/accounts-settings-section-BVr9-FZJ.js +0 -1
  130. package/dist/web/assets/ai-settings-section-B1hQyjAd.js +0 -1
  131. package/dist/web/assets/ai-settings-section-X2UOfSaS.js +0 -1
  132. package/dist/web/assets/api-client-pQL53cDQ.js +0 -1
  133. package/dist/web/assets/chat-tab-DQJfjm8E.js +0 -15
  134. package/dist/web/assets/extension-manager-section-DG1guILi.js +0 -1
  135. package/dist/web/assets/index-DU68xk06.js +0 -52
  136. package/dist/web/assets/keybindings-store-DCRsnKg5.js +0 -1
  137. package/dist/web/assets/markdown-renderer-Bcaf9_bA.js +0 -1
  138. package/dist/web/assets/markdown-renderer-DGJp8HC3.js +0 -3
  139. package/dist/web/assets/notification-store-BXt54VIn.js +0 -1
  140. package/dist/web/assets/panel-store-CboGJAwh.js +0 -1
  141. package/dist/web/assets/system-monitor-tab-DiSA490Z.js +0 -1
  142. package/dist/web/assets/ws-auth-CEYVQ8NH.js +0 -1
  143. package/packages/vscode-compat/node_modules/.bin/tsc.bunx +0 -0
  144. package/packages/vscode-compat/node_modules/.bin/tsc.exe +0 -0
  145. package/packages/vscode-compat/node_modules/.bin/tsserver.bunx +0 -0
  146. package/packages/vscode-compat/node_modules/.bin/tsserver.exe +0 -0
  147. package/src/web/hooks/use-delayed-flag.ts +0 -35
@@ -1 +1 @@
1
- import{o as e}from"./rolldown-runtime-FhOqtrmT.js";import{C as t,S as n}from"./vendor-markdown-CuWCHIVb.js";import"./vendor-ui-DmZ-Mb-H.js";import{$n as r,Ct as i,Dr as a,Fr as o,Gn as s,Ir as c,Pr as l,Tt as u,er as d,ln as f,mn as p,or as m,st as h,xn as g,yt as _,zn as v}from"./icons-DQjqk0lJ.js";import"./api-client-C1ZwRL4Y.js";import"./portal-container-context-BarKmT4S.js";import{t as y}from"./button-B9CTrsEf.js";import{Gt as b,Ht as x,Kt as S,Ut as C,Vt as w,Wt as T,Xt as E,zt as D}from"./index-DU68xk06.js";import{o as O}from"./can-open-in-ppm-cv5--yHX.js";import{a as k,i as A,n as j,r as M,t as ee}from"./media-url-yfqHpPz0.js";var N=e(t(),1),P={rotation:0,flipH:!1,flipV:!1};function F(e){return{...e,rotation:(e.rotation+90)%360}}function te(e){return{...e,rotation:(e.rotation+270)%360}}function I(e){return e.rotation===0&&!e.flipH&&!e.flipV}function ne(e){let t=[];return e.rotation&&t.push(`rotate(${e.rotation}deg)`),e.flipH&&t.push(`scaleX(-1)`),e.flipV&&t.push(`scaleY(-1)`),t.length?t.join(` `):`none`}function re(e,t){return e.rotation===90||e.rotation===270?{maxWidth:t.height,maxHeight:t.width}:{maxWidth:t.width,maxHeight:t.height}}var L=[[`Space / K`,`Play / pause`],[`← / →`,`Seek 5s`],[`J / L or Shift+← / →`,`Seek 10s`],[`↑ / ↓`,`Volume`],[`M`,`Mute`],[`F`,`Fullscreen`],[`< / >`,`Speed`],[`R / Shift+R`,`Rotate`]];function R(e){return(0,N.useCallback)(t=>{let n=t.target;if(n.tagName===`INPUT`||n.tagName===`TEXTAREA`||n.isContentEditable||t.ctrlKey||t.metaKey||t.altKey)return;let r=!0;switch(t.key){case` `:case`k`:case`K`:e.togglePlay();break;case`ArrowLeft`:e.seekBy(t.shiftKey?-10:-5);break;case`ArrowRight`:e.seekBy(t.shiftKey?10:5);break;case`j`:case`J`:e.seekBy(-10);break;case`l`:case`L`:e.seekBy(10);break;case`ArrowUp`:e.volumeBy(.1);break;case`ArrowDown`:e.volumeBy(-.1);break;case`m`:case`M`:e.toggleMute();break;case`f`:case`F`:e.toggleFullscreen();break;case`<`:case`,`:e.speedStep(-1);break;case`>`:case`.`:e.speedStep(1);break;case`r`:e.rotate(1);break;case`R`:e.rotate(-1);break;default:r=!1}r&&t.preventDefault()},[e])}var z=n(),B=[.25,.5,.75,1,1.25,1.5,2,3];function V({speed:e,onSpeed:t,transform:n,onRotate:o,onFlipH:s,onFlipV:c,onResetTransform:l}){return(0,z.jsxs)(D,{children:[(0,z.jsx)(E,{asChild:!0,children:(0,z.jsxs)(y,{variant:`ghost`,size:`icon`,className:`size-11 shrink-0 relative`,"aria-label":`Playback settings`,children:[(0,z.jsx)(m,{className:`size-5`}),e!==1&&(0,z.jsxs)(`span`,{className:`absolute -top-0.5 -right-0.5 text-[10px] leading-none px-1 rounded bg-primary text-primary-foreground tabular-nums`,children:[e,`x`]})]})}),(0,z.jsxs)(w,{align:`end`,className:`w-60 max-h-[70vh] overflow-y-auto`,children:[(0,z.jsx)(C,{children:`Speed`}),(0,z.jsx)(T,{value:String(e),onValueChange:e=>t(Number(e)),children:(0,z.jsx)(`div`,{className:`grid grid-cols-4 gap-1 px-1 pb-1`,children:B.map(e=>(0,z.jsxs)(b,{value:String(e),className:`justify-center pl-2 min-h-11 text-xs tabular-nums data-[state=checked]:bg-accent [&>span:first-child]:hidden`,children:[e,`x`]},e))})}),(0,z.jsx)(S,{}),(0,z.jsx)(C,{children:`Orientation`}),(0,z.jsxs)(x,{className:`min-h-11`,onSelect:e=>{e.preventDefault(),o(1)},children:[(0,z.jsx)(d,{className:`size-4`}),` Rotate right `,(0,z.jsx)(`span`,{className:`ml-auto text-xs text-text-subtle`,children:`R`})]}),(0,z.jsxs)(x,{className:`min-h-11`,onSelect:e=>{e.preventDefault(),o(-1)},children:[(0,z.jsx)(r,{className:`size-4`}),` Rotate left `,(0,z.jsx)(`span`,{className:`ml-auto text-xs text-text-subtle`,children:`⇧R`})]}),(0,z.jsxs)(x,{className:`min-h-11`,onSelect:e=>{e.preventDefault(),s()},children:[(0,z.jsx)(i,{className:`size-4`}),` Flip horizontal `,n.flipH&&(0,z.jsx)(`span`,{className:`ml-auto text-xs`,children:`✓`})]}),(0,z.jsxs)(x,{className:`min-h-11`,onSelect:e=>{e.preventDefault(),c()},children:[(0,z.jsx)(u,{className:`size-4`}),` Flip vertical `,n.flipV&&(0,z.jsx)(`span`,{className:`ml-auto text-xs`,children:`✓`})]}),!I(n)&&(0,z.jsxs)(x,{className:`min-h-11`,onSelect:l,children:[(0,z.jsx)(a,{className:`size-4`}),` Reset orientation`]}),(0,z.jsx)(S,{}),(0,z.jsx)(C,{children:`Keyboard`}),(0,z.jsx)(`div`,{className:`px-2 pb-2 grid grid-cols-[auto_1fr] gap-x-3 gap-y-1 text-xs text-text-secondary`,children:L.map(([e,t])=>(0,z.jsxs)(`div`,{className:`contents`,children:[(0,z.jsx)(`kbd`,{className:`font-mono text-text-subtle whitespace-nowrap`,children:e}),(0,z.jsx)(`span`,{children:t})]},e))})]})]})}function ie(e){let t=e.muted||e.volume===0?c:e.volume<.5?l:o,n=t=>e.onSeek(Number(t.target.value));return(0,z.jsxs)(`div`,{className:`flex items-center gap-1 px-2 py-1 bg-surface border-t border-border`,children:[(0,z.jsx)(y,{variant:`ghost`,size:`icon`,className:`size-11 shrink-0`,onClick:e.onTogglePlay,"aria-label":e.playing?`Pause`:`Play`,children:e.playing?(0,z.jsx)(v,{className:`size-5`}):(0,z.jsx)(s,{className:`size-5`})}),(0,z.jsxs)(`span`,{className:`text-xs tabular-nums text-text-secondary shrink-0 min-w-[4.5rem] text-center`,children:[H(e.position),e.duration?` / ${H(e.duration)}`:``]}),e.duration?(0,z.jsx)(`input`,{type:`range`,min:0,max:e.duration,step:.1,value:Math.min(e.position,e.duration),"aria-label":`Seek`,className:`flex-1 h-11 accent-primary cursor-pointer min-w-0`,onChange:t=>e.onScrub(Number(t.target.value)),onPointerUp:n,onKeyUp:n,onTouchEnd:n}):(0,z.jsx)(`div`,{className:`flex-1`}),(0,z.jsxs)(`div`,{className:`flex items-center shrink-0`,children:[(0,z.jsx)(y,{variant:`ghost`,size:`icon`,className:`size-11`,onClick:e.onToggleMute,"aria-label":e.muted?`Unmute`:`Mute`,children:(0,z.jsx)(t,{className:`size-5`})}),(0,z.jsx)(`input`,{type:`range`,min:0,max:1,step:.05,value:e.muted?0:e.volume,"aria-label":`Volume`,className:`hidden sm:block w-20 h-11 accent-primary cursor-pointer`,onChange:t=>e.onVolume(Number(t.target.value))})]}),(0,z.jsx)(V,{speed:e.speed,onSpeed:e.onSpeed,transform:e.transform,onRotate:e.onRotate,onFlipH:e.onFlipH,onFlipV:e.onFlipV,onResetTransform:e.onResetTransform}),(0,z.jsx)(y,{variant:`ghost`,size:`icon`,className:`size-11 shrink-0`,onClick:e.onToggleFullscreen,"aria-label":e.fullscreen?`Exit fullscreen`:`Fullscreen`,children:e.fullscreen?(0,z.jsx)(g,{className:`size-5`}):(0,z.jsx)(p,{className:`size-5`})})]})}function H(e){let t=Math.max(0,Math.floor(Number.isFinite(e)?e:0)),n=Math.floor(t/3600),r=Math.floor(t%3600/60),i=t%60,a=n?String(r).padStart(2,`0`):String(r);return`${n?`${n}:`:``}${a}:${String(i).padStart(2,`0`)}`}function U({filePath:e,projectName:t,mode:n,probeDuration:r=null,onNativeError:i}){let a=(0,N.useRef)(null),o=(0,N.useRef)(null),s=(0,N.useRef)(null),c=(0,N.useRef)(!0),[l]=(0,N.useState)(ee),[u,d]=(0,N.useState)(0),[p,m]=(0,N.useState)(0),[h,g]=(0,N.useState)(null),[_,v]=(0,N.useState)(!1),[b,x]=(0,N.useState)(!0),[S,C]=(0,N.useState)(null),[w,T]=(0,N.useState)(1),[E,D]=(0,N.useState)(!1),[O,j]=(0,N.useState)(1),[I,L]=(0,N.useState)(P),[V,H]=(0,N.useState)(!1),[U,W]=(0,N.useState)({width:0,height:0}),[G,K]=(0,N.useState)(null),q=n===`transcode`,J=q?k(e,t,u,l):M(e,t),Y=q?r:h,X=S??(q?u+p:p);(0,N.useEffect)(()=>{m(0),x(!0),K(null)},[J]),(0,N.useEffect)(()=>{d(0),g(null),L(P)},[e,n]),(0,N.useEffect)(()=>{let e=s.current;e&&(e.volume=w,e.muted=E)},[w,E]),(0,N.useEffect)(()=>{let e=s.current;e&&(e.playbackRate=O)},[O,J]),(0,N.useEffect)(()=>{let n=s.current,r=()=>{q&&A(e,t,l)};return window.addEventListener(`pagehide`,r),()=>{window.removeEventListener(`pagehide`,r),n&&!n.isConnected&&(n.pause(),n.removeAttribute(`src`),n.load(),r())}},[e,t,q,l]),(0,N.useEffect)(()=>{let e=()=>H(document.fullscreenElement===a.current);return document.addEventListener(`fullscreenchange`,e),()=>document.removeEventListener(`fullscreenchange`,e)},[]),(0,N.useEffect)(()=>{let e=o.current;if(!e)return;let t=new ResizeObserver(()=>W({width:e.clientWidth,height:e.clientHeight}));return t.observe(e),()=>t.disconnect()},[]);let Z=(0,N.useCallback)(e=>{let t=s.current,n=Math.max(0,Y?Math.min(e,Y-.5):e);if(C(null),!q){t&&(t.currentTime=n);return}Math.abs(n-(u+(t?.currentTime??0)))<.5||d(n)},[Y,q,u]),Q=(0,N.useMemo)(()=>({togglePlay:()=>{let e=s.current;e&&(c.current=e.paused,e.paused?e.play().catch(()=>{}):e.pause())},seekBy:e=>Z((q?u:0)+(s.current?.currentTime??0)+e),toggleFullscreen:()=>{document.fullscreenElement?document.exitFullscreen():a.current?.requestFullscreen?.()},toggleMute:()=>D(e=>!e),volumeBy:e=>{D(!1),T(t=>Math.min(1,Math.max(0,Math.round((t+e)*100)/100)))},speedStep:e=>j(t=>{let n=B.indexOf(t);return B[Math.min(B.length-1,Math.max(0,(n===-1?3:n)+e))]??1}),rotate:e=>L(t=>e===1?F(t):te(t))}),[Z,q,u]),ae=R(Q),$=re(I,U),oe=()=>{K(null),d(e=>e+.001)};return(0,z.jsxs)(`div`,{ref:a,"data-testid":`video-player`,tabIndex:0,onKeyDown:ae,className:`flex flex-col h-full bg-black outline-none focus-visible:ring-1 focus-visible:ring-primary/60`,children:[(0,z.jsxs)(`div`,{ref:o,className:`relative flex-1 min-h-0 flex items-center justify-center overflow-hidden`,onClick:e=>{a.current?.focus(),e.detail===1&&Q.togglePlay()},onDoubleClick:Q.toggleFullscreen,children:[(0,z.jsx)(`video`,{ref:s,src:J,autoPlay:!0,playsInline:!0,preload:q?`auto`:`metadata`,style:{transform:ne(I),maxWidth:$.maxWidth||void 0,maxHeight:$.maxHeight||void 0},onLoadedMetadata:e=>{let t=e.currentTarget;t.playbackRate=O,t.volume=w,t.muted=E,!q&&Number.isFinite(t.duration)&&g(t.duration),q&&!c.current&&t.pause()},onPlay:()=>v(!0),onPause:()=>v(!1),onWaiting:()=>x(!0),onPlaying:()=>x(!1),onCanPlay:()=>x(!1),onTimeUpdate:e=>m(e.currentTarget.currentTime),onEnded:()=>{v(!1),c.current=!1},onError:()=>{q?K(`Transcoding failed. ffmpeg may have stopped or the file is corrupt.`):i?.()}}),b&&!G&&(0,z.jsx)(f,{className:`absolute size-8 animate-spin text-white/80 pointer-events-none`}),G&&(0,z.jsxs)(`div`,{className:`absolute inset-0 flex flex-col items-center justify-center gap-3 bg-black/85 text-text-secondary p-4 text-center`,onClick:e=>e.stopPropagation(),onDoubleClick:e=>e.stopPropagation(),children:[(0,z.jsx)(`p`,{className:`text-sm`,children:G}),(0,z.jsx)(y,{variant:`outline`,size:`sm`,className:`h-11 px-4`,onClick:oe,children:`Retry`})]})]}),(0,z.jsx)(ie,{playing:_,position:X,duration:Y,volume:w,muted:E,speed:O,transform:I,fullscreen:V,onTogglePlay:Q.togglePlay,onScrub:C,onSeek:Z,onVolume:e=>{T(e),D(e===0)},onToggleMute:Q.toggleMute,onSpeed:j,onRotate:Q.rotate,onFlipH:()=>L(e=>({...e,flipH:!e.flipH})),onFlipV:()=>L(e=>({...e,flipV:!e.flipV})),onResetTransform:()=>L(P),onToggleFullscreen:Q.toggleFullscreen})]})}var W=new Set([`mp4`,`m4v`,`webm`,`ogg`,`ogv`,`mov`]);function G({filePath:e,projectName:t}){let n=O(e),[r,i]=(0,N.useState)(()=>W.has(n)?{kind:`native`}:{kind:`probing`});return(0,N.useEffect)(()=>{i(W.has(O(e))?{kind:`native`}:{kind:`probing`})},[e]),(0,N.useEffect)(()=>{if(r.kind!==`probing`)return;let n=!1;return j(e,t).then(e=>{n||i(e.transcodable?{kind:`transcode`,probe:e}:{kind:`unsupported`,probe:e})}).catch(()=>{n||i({kind:`unsupported`,probe:null})}),()=>{n=!0}},[r.kind,e,t]),r.kind===`probing`?(0,z.jsx)(`div`,{className:`flex items-center justify-center h-full`,children:(0,z.jsx)(f,{className:`size-5 animate-spin text-text-subtle`})}):r.kind===`transcode`?(0,z.jsx)(U,{filePath:e,projectName:t,mode:`transcode`,probeDuration:r.probe.duration}):r.kind===`unsupported`?(0,z.jsx)(K,{filePath:e,projectName:t,probe:r.probe}):(0,z.jsx)(U,{filePath:e,projectName:t,mode:`native`,onNativeError:()=>i({kind:`probing`})})}function K({filePath:e,projectName:t,probe:n}){let r=[n?.video?.codec,n?.audio?.codec].filter(Boolean).join(` + `);return(0,z.jsxs)(`div`,{className:`flex flex-col items-center justify-center h-full gap-3 text-text-secondary p-6 text-center`,children:[(0,z.jsx)(_,{className:`size-10 text-text-subtle`}),(0,z.jsxs)(`p`,{className:`text-sm`,children:[`This video cannot be played in the browser`,r?` (${r})`:``,`.`]}),(0,z.jsxs)(`p`,{className:`text-xs text-text-subtle max-w-sm`,children:[`Install `,(0,z.jsx)(`code`,{children:`ffmpeg`}),` on the PPM server (e.g. `,(0,z.jsx)(`code`,{children:`winget install ffmpeg`}),`, `,(0,z.jsx)(`code`,{children:`brew install ffmpeg`}),`,`,` `,(0,z.jsx)(`code`,{children:`apt install ffmpeg`}),`) and restart PPM to transcode it on the fly.`]}),(0,z.jsx)(y,{asChild:!0,variant:`outline`,size:`sm`,className:`h-11 px-4`,children:(0,z.jsxs)(`a`,{href:`${M(e,t)}&download=true`,download:!0,children:[(0,z.jsx)(h,{className:`size-4 mr-2`}),` Download file`]})})]})}export{G as VideoPreview};
1
+ import{o as e}from"./rolldown-runtime-FhOqtrmT.js";import{C as t,S as n}from"./vendor-markdown-CuWCHIVb.js";import"./vendor-ui-DmZ-Mb-H.js";import{$n as r,Ct as i,Dr as a,Fr as o,Gn as s,Ir as c,Pr as l,Tt as u,er as d,ln as f,mn as p,or as m,st as h,xn as g,yt as _,zn as v}from"./icons-DQjqk0lJ.js";import"./api-client-Jn3njtNw.js";import"./portal-container-context-BarKmT4S.js";import{t as y}from"./button-B9CTrsEf.js";import{Gt as b,Ht as x,Kt as S,Ut as C,Vt as w,Wt as T,Xt as E,zt as D}from"./index-CN0G8uYY.js";import{o as O}from"./can-open-in-ppm-cv5--yHX.js";import{a as k,i as A,n as j,r as M,t as ee}from"./media-url-DKK0YHxL.js";var N=e(t(),1),P={rotation:0,flipH:!1,flipV:!1};function F(e){return{...e,rotation:(e.rotation+90)%360}}function te(e){return{...e,rotation:(e.rotation+270)%360}}function I(e){return e.rotation===0&&!e.flipH&&!e.flipV}function ne(e){let t=[];return e.rotation&&t.push(`rotate(${e.rotation}deg)`),e.flipH&&t.push(`scaleX(-1)`),e.flipV&&t.push(`scaleY(-1)`),t.length?t.join(` `):`none`}function re(e,t){return e.rotation===90||e.rotation===270?{maxWidth:t.height,maxHeight:t.width}:{maxWidth:t.width,maxHeight:t.height}}var L=[[`Space / K`,`Play / pause`],[`← / →`,`Seek 5s`],[`J / L or Shift+← / →`,`Seek 10s`],[`↑ / ↓`,`Volume`],[`M`,`Mute`],[`F`,`Fullscreen`],[`< / >`,`Speed`],[`R / Shift+R`,`Rotate`]];function R(e){return(0,N.useCallback)(t=>{let n=t.target;if(n.tagName===`INPUT`||n.tagName===`TEXTAREA`||n.isContentEditable||t.ctrlKey||t.metaKey||t.altKey)return;let r=!0;switch(t.key){case` `:case`k`:case`K`:e.togglePlay();break;case`ArrowLeft`:e.seekBy(t.shiftKey?-10:-5);break;case`ArrowRight`:e.seekBy(t.shiftKey?10:5);break;case`j`:case`J`:e.seekBy(-10);break;case`l`:case`L`:e.seekBy(10);break;case`ArrowUp`:e.volumeBy(.1);break;case`ArrowDown`:e.volumeBy(-.1);break;case`m`:case`M`:e.toggleMute();break;case`f`:case`F`:e.toggleFullscreen();break;case`<`:case`,`:e.speedStep(-1);break;case`>`:case`.`:e.speedStep(1);break;case`r`:e.rotate(1);break;case`R`:e.rotate(-1);break;default:r=!1}r&&t.preventDefault()},[e])}var z=n(),B=[.25,.5,.75,1,1.25,1.5,2,3];function V({speed:e,onSpeed:t,transform:n,onRotate:o,onFlipH:s,onFlipV:c,onResetTransform:l}){return(0,z.jsxs)(D,{children:[(0,z.jsx)(E,{asChild:!0,children:(0,z.jsxs)(y,{variant:`ghost`,size:`icon`,className:`size-11 shrink-0 relative`,"aria-label":`Playback settings`,children:[(0,z.jsx)(m,{className:`size-5`}),e!==1&&(0,z.jsxs)(`span`,{className:`absolute -top-0.5 -right-0.5 text-[10px] leading-none px-1 rounded bg-primary text-primary-foreground tabular-nums`,children:[e,`x`]})]})}),(0,z.jsxs)(w,{align:`end`,className:`w-60 max-h-[70vh] overflow-y-auto`,children:[(0,z.jsx)(C,{children:`Speed`}),(0,z.jsx)(T,{value:String(e),onValueChange:e=>t(Number(e)),children:(0,z.jsx)(`div`,{className:`grid grid-cols-4 gap-1 px-1 pb-1`,children:B.map(e=>(0,z.jsxs)(b,{value:String(e),className:`justify-center pl-2 min-h-11 text-xs tabular-nums data-[state=checked]:bg-accent [&>span:first-child]:hidden`,children:[e,`x`]},e))})}),(0,z.jsx)(S,{}),(0,z.jsx)(C,{children:`Orientation`}),(0,z.jsxs)(x,{className:`min-h-11`,onSelect:e=>{e.preventDefault(),o(1)},children:[(0,z.jsx)(d,{className:`size-4`}),` Rotate right `,(0,z.jsx)(`span`,{className:`ml-auto text-xs text-text-subtle`,children:`R`})]}),(0,z.jsxs)(x,{className:`min-h-11`,onSelect:e=>{e.preventDefault(),o(-1)},children:[(0,z.jsx)(r,{className:`size-4`}),` Rotate left `,(0,z.jsx)(`span`,{className:`ml-auto text-xs text-text-subtle`,children:`⇧R`})]}),(0,z.jsxs)(x,{className:`min-h-11`,onSelect:e=>{e.preventDefault(),s()},children:[(0,z.jsx)(i,{className:`size-4`}),` Flip horizontal `,n.flipH&&(0,z.jsx)(`span`,{className:`ml-auto text-xs`,children:`✓`})]}),(0,z.jsxs)(x,{className:`min-h-11`,onSelect:e=>{e.preventDefault(),c()},children:[(0,z.jsx)(u,{className:`size-4`}),` Flip vertical `,n.flipV&&(0,z.jsx)(`span`,{className:`ml-auto text-xs`,children:`✓`})]}),!I(n)&&(0,z.jsxs)(x,{className:`min-h-11`,onSelect:l,children:[(0,z.jsx)(a,{className:`size-4`}),` Reset orientation`]}),(0,z.jsx)(S,{}),(0,z.jsx)(C,{children:`Keyboard`}),(0,z.jsx)(`div`,{className:`px-2 pb-2 grid grid-cols-[auto_1fr] gap-x-3 gap-y-1 text-xs text-text-secondary`,children:L.map(([e,t])=>(0,z.jsxs)(`div`,{className:`contents`,children:[(0,z.jsx)(`kbd`,{className:`font-mono text-text-subtle whitespace-nowrap`,children:e}),(0,z.jsx)(`span`,{children:t})]},e))})]})]})}function ie(e){let t=e.muted||e.volume===0?c:e.volume<.5?l:o,n=t=>e.onSeek(Number(t.target.value));return(0,z.jsxs)(`div`,{className:`flex items-center gap-1 px-2 py-1 bg-surface border-t border-border`,children:[(0,z.jsx)(y,{variant:`ghost`,size:`icon`,className:`size-11 shrink-0`,onClick:e.onTogglePlay,"aria-label":e.playing?`Pause`:`Play`,children:e.playing?(0,z.jsx)(v,{className:`size-5`}):(0,z.jsx)(s,{className:`size-5`})}),(0,z.jsxs)(`span`,{className:`text-xs tabular-nums text-text-secondary shrink-0 min-w-[4.5rem] text-center`,children:[H(e.position),e.duration?` / ${H(e.duration)}`:``]}),e.duration?(0,z.jsx)(`input`,{type:`range`,min:0,max:e.duration,step:.1,value:Math.min(e.position,e.duration),"aria-label":`Seek`,className:`flex-1 h-11 accent-primary cursor-pointer min-w-0`,onChange:t=>e.onScrub(Number(t.target.value)),onPointerUp:n,onKeyUp:n,onTouchEnd:n}):(0,z.jsx)(`div`,{className:`flex-1`}),(0,z.jsxs)(`div`,{className:`flex items-center shrink-0`,children:[(0,z.jsx)(y,{variant:`ghost`,size:`icon`,className:`size-11`,onClick:e.onToggleMute,"aria-label":e.muted?`Unmute`:`Mute`,children:(0,z.jsx)(t,{className:`size-5`})}),(0,z.jsx)(`input`,{type:`range`,min:0,max:1,step:.05,value:e.muted?0:e.volume,"aria-label":`Volume`,className:`hidden sm:block w-20 h-11 accent-primary cursor-pointer`,onChange:t=>e.onVolume(Number(t.target.value))})]}),(0,z.jsx)(V,{speed:e.speed,onSpeed:e.onSpeed,transform:e.transform,onRotate:e.onRotate,onFlipH:e.onFlipH,onFlipV:e.onFlipV,onResetTransform:e.onResetTransform}),(0,z.jsx)(y,{variant:`ghost`,size:`icon`,className:`size-11 shrink-0`,onClick:e.onToggleFullscreen,"aria-label":e.fullscreen?`Exit fullscreen`:`Fullscreen`,children:e.fullscreen?(0,z.jsx)(g,{className:`size-5`}):(0,z.jsx)(p,{className:`size-5`})})]})}function H(e){let t=Math.max(0,Math.floor(Number.isFinite(e)?e:0)),n=Math.floor(t/3600),r=Math.floor(t%3600/60),i=t%60,a=n?String(r).padStart(2,`0`):String(r);return`${n?`${n}:`:``}${a}:${String(i).padStart(2,`0`)}`}function U({filePath:e,projectName:t,mode:n,probeDuration:r=null,onNativeError:i}){let a=(0,N.useRef)(null),o=(0,N.useRef)(null),s=(0,N.useRef)(null),c=(0,N.useRef)(!0),[l]=(0,N.useState)(ee),[u,d]=(0,N.useState)(0),[p,m]=(0,N.useState)(0),[h,g]=(0,N.useState)(null),[_,v]=(0,N.useState)(!1),[b,x]=(0,N.useState)(!0),[S,C]=(0,N.useState)(null),[w,T]=(0,N.useState)(1),[E,D]=(0,N.useState)(!1),[O,j]=(0,N.useState)(1),[I,L]=(0,N.useState)(P),[V,H]=(0,N.useState)(!1),[U,W]=(0,N.useState)({width:0,height:0}),[G,K]=(0,N.useState)(null),q=n===`transcode`,J=q?k(e,t,u,l):M(e,t),Y=q?r:h,X=S??(q?u+p:p);(0,N.useEffect)(()=>{m(0),x(!0),K(null)},[J]),(0,N.useEffect)(()=>{d(0),g(null),L(P)},[e,n]),(0,N.useEffect)(()=>{let e=s.current;e&&(e.volume=w,e.muted=E)},[w,E]),(0,N.useEffect)(()=>{let e=s.current;e&&(e.playbackRate=O)},[O,J]),(0,N.useEffect)(()=>{let n=s.current,r=()=>{q&&A(e,t,l)};return window.addEventListener(`pagehide`,r),()=>{window.removeEventListener(`pagehide`,r),n&&!n.isConnected&&(n.pause(),n.removeAttribute(`src`),n.load(),r())}},[e,t,q,l]),(0,N.useEffect)(()=>{let e=()=>H(document.fullscreenElement===a.current);return document.addEventListener(`fullscreenchange`,e),()=>document.removeEventListener(`fullscreenchange`,e)},[]),(0,N.useEffect)(()=>{let e=o.current;if(!e)return;let t=new ResizeObserver(()=>W({width:e.clientWidth,height:e.clientHeight}));return t.observe(e),()=>t.disconnect()},[]);let Z=(0,N.useCallback)(e=>{let t=s.current,n=Math.max(0,Y?Math.min(e,Y-.5):e);if(C(null),!q){t&&(t.currentTime=n);return}Math.abs(n-(u+(t?.currentTime??0)))<.5||d(n)},[Y,q,u]),Q=(0,N.useMemo)(()=>({togglePlay:()=>{let e=s.current;e&&(c.current=e.paused,e.paused?e.play().catch(()=>{}):e.pause())},seekBy:e=>Z((q?u:0)+(s.current?.currentTime??0)+e),toggleFullscreen:()=>{document.fullscreenElement?document.exitFullscreen():a.current?.requestFullscreen?.()},toggleMute:()=>D(e=>!e),volumeBy:e=>{D(!1),T(t=>Math.min(1,Math.max(0,Math.round((t+e)*100)/100)))},speedStep:e=>j(t=>{let n=B.indexOf(t);return B[Math.min(B.length-1,Math.max(0,(n===-1?3:n)+e))]??1}),rotate:e=>L(t=>e===1?F(t):te(t))}),[Z,q,u]),ae=R(Q),$=re(I,U),oe=()=>{K(null),d(e=>e+.001)};return(0,z.jsxs)(`div`,{ref:a,"data-testid":`video-player`,tabIndex:0,onKeyDown:ae,className:`flex flex-col h-full bg-black outline-none focus-visible:ring-1 focus-visible:ring-primary/60`,children:[(0,z.jsxs)(`div`,{ref:o,className:`relative flex-1 min-h-0 flex items-center justify-center overflow-hidden`,onClick:e=>{a.current?.focus(),e.detail===1&&Q.togglePlay()},onDoubleClick:Q.toggleFullscreen,children:[(0,z.jsx)(`video`,{ref:s,src:J,autoPlay:!0,playsInline:!0,preload:q?`auto`:`metadata`,style:{transform:ne(I),maxWidth:$.maxWidth||void 0,maxHeight:$.maxHeight||void 0},onLoadedMetadata:e=>{let t=e.currentTarget;t.playbackRate=O,t.volume=w,t.muted=E,!q&&Number.isFinite(t.duration)&&g(t.duration),q&&!c.current&&t.pause()},onPlay:()=>v(!0),onPause:()=>v(!1),onWaiting:()=>x(!0),onPlaying:()=>x(!1),onCanPlay:()=>x(!1),onTimeUpdate:e=>m(e.currentTarget.currentTime),onEnded:()=>{v(!1),c.current=!1},onError:()=>{q?K(`Transcoding failed. ffmpeg may have stopped or the file is corrupt.`):i?.()}}),b&&!G&&(0,z.jsx)(f,{className:`absolute size-8 animate-spin text-white/80 pointer-events-none`}),G&&(0,z.jsxs)(`div`,{className:`absolute inset-0 flex flex-col items-center justify-center gap-3 bg-black/85 text-text-secondary p-4 text-center`,onClick:e=>e.stopPropagation(),onDoubleClick:e=>e.stopPropagation(),children:[(0,z.jsx)(`p`,{className:`text-sm`,children:G}),(0,z.jsx)(y,{variant:`outline`,size:`sm`,className:`h-11 px-4`,onClick:oe,children:`Retry`})]})]}),(0,z.jsx)(ie,{playing:_,position:X,duration:Y,volume:w,muted:E,speed:O,transform:I,fullscreen:V,onTogglePlay:Q.togglePlay,onScrub:C,onSeek:Z,onVolume:e=>{T(e),D(e===0)},onToggleMute:Q.toggleMute,onSpeed:j,onRotate:Q.rotate,onFlipH:()=>L(e=>({...e,flipH:!e.flipH})),onFlipV:()=>L(e=>({...e,flipV:!e.flipV})),onResetTransform:()=>L(P),onToggleFullscreen:Q.toggleFullscreen})]})}var W=new Set([`mp4`,`m4v`,`webm`,`ogg`,`ogv`,`mov`]);function G({filePath:e,projectName:t}){let n=O(e),[r,i]=(0,N.useState)(()=>W.has(n)?{kind:`native`}:{kind:`probing`});return(0,N.useEffect)(()=>{i(W.has(O(e))?{kind:`native`}:{kind:`probing`})},[e]),(0,N.useEffect)(()=>{if(r.kind!==`probing`)return;let n=!1;return j(e,t).then(e=>{n||i(e.transcodable?{kind:`transcode`,probe:e}:{kind:`unsupported`,probe:e})}).catch(()=>{n||i({kind:`unsupported`,probe:null})}),()=>{n=!0}},[r.kind,e,t]),r.kind===`probing`?(0,z.jsx)(`div`,{className:`flex items-center justify-center h-full`,children:(0,z.jsx)(f,{className:`size-5 animate-spin text-text-subtle`})}):r.kind===`transcode`?(0,z.jsx)(U,{filePath:e,projectName:t,mode:`transcode`,probeDuration:r.probe.duration}):r.kind===`unsupported`?(0,z.jsx)(K,{filePath:e,projectName:t,probe:r.probe}):(0,z.jsx)(U,{filePath:e,projectName:t,mode:`native`,onNativeError:()=>i({kind:`probing`})})}function K({filePath:e,projectName:t,probe:n}){let r=[n?.video?.codec,n?.audio?.codec].filter(Boolean).join(` + `);return(0,z.jsxs)(`div`,{className:`flex flex-col items-center justify-center h-full gap-3 text-text-secondary p-6 text-center`,children:[(0,z.jsx)(_,{className:`size-10 text-text-subtle`}),(0,z.jsxs)(`p`,{className:`text-sm`,children:[`This video cannot be played in the browser`,r?` (${r})`:``,`.`]}),(0,z.jsxs)(`p`,{className:`text-xs text-text-subtle max-w-sm`,children:[`Install `,(0,z.jsx)(`code`,{children:`ffmpeg`}),` on the PPM server (e.g. `,(0,z.jsx)(`code`,{children:`winget install ffmpeg`}),`, `,(0,z.jsx)(`code`,{children:`brew install ffmpeg`}),`,`,` `,(0,z.jsx)(`code`,{children:`apt install ffmpeg`}),`) and restart PPM to transcode it on the fly.`]}),(0,z.jsx)(y,{asChild:!0,variant:`outline`,size:`sm`,className:`h-11 px-4`,children:(0,z.jsxs)(`a`,{href:`${M(e,t)}&download=true`,download:!0,children:[(0,z.jsx)(h,{className:`size-4 mr-2`}),` Download file`]})})]})}export{G as VideoPreview};
@@ -1 +1 @@
1
- import{o as e}from"./rolldown-runtime-FhOqtrmT.js";import{C as t}from"./vendor-markdown-CuWCHIVb.js";import{f as n}from"./panel-store-CboGJAwh.js";var r=e(t(),1),i=new Map,a=new Map,o=new Set;function s(e,t){t?i.set(e,t):i.delete(e);for(let e of o)e()}function c(e,t=2e3){let n=p(e);return n?Promise.resolve(n):new Promise(n=>{let r=e=>{o.delete(i),clearTimeout(a),n(e)},i=()=>{let t=p(e);t&&r(t)},a=setTimeout(()=>r(null),t);o.add(i)})}var l=new Set;function u(e){l.add(e)}function d(e){return l.has(e)}function f(e){l.delete(e)}function p(e){return i.get(e)??null}function m(e,t){if((a.get(e)??null)!==t){t?a.set(e,t):a.delete(e);for(let e of o)e()}}function h(e){return e?a.get(e)??null:null}function g(e){return o.add(e),()=>{o.delete(e)}}function _(e){return(0,r.useSyncExternalStore)(g,(0,r.useCallback)(()=>h(e),[e]))}function v(e){return _(n(e))?.pipWindow.document.body}export{m as a,c,s as i,h as l,d as n,v as o,u as r,_ as s,f as t,p as u};
1
+ import{o as e}from"./rolldown-runtime-FhOqtrmT.js";import{C as t}from"./vendor-markdown-CuWCHIVb.js";import{f as n}from"./panel-store-D20Zuf3i.js";var r=e(t(),1),i=new Map,a=new Map,o=new Set;function s(e,t){t?i.set(e,t):i.delete(e);for(let e of o)e()}function c(e,t=2e3){let n=p(e);return n?Promise.resolve(n):new Promise(n=>{let r=e=>{o.delete(i),clearTimeout(a),n(e)},i=()=>{let t=p(e);t&&r(t)},a=setTimeout(()=>r(null),t);o.add(i)})}var l=new Set;function u(e){l.add(e)}function d(e){return l.has(e)}function f(e){l.delete(e)}function p(e){return i.get(e)??null}function m(e,t){if((a.get(e)??null)!==t){t?a.set(e,t):a.delete(e);for(let e of o)e()}}function h(e){return e?a.get(e)??null:null}function g(e){return o.add(e),()=>{o.delete(e)}}function _(e){return(0,r.useSyncExternalStore)(g,(0,r.useCallback)(()=>h(e),[e]))}function v(e){return _(n(e))?.pipWindow.document.body}export{m as a,c,s as i,h as l,d as n,v as o,u as r,_ as s,f as t,p as u};
@@ -0,0 +1 @@
1
+ import{r as e}from"./api-client-Jn3njtNw.js";function t(t){let n=e();return!n||/[?&]token=/.test(t)?t:`${t}${t.includes(`?`)?`&`:`?`}token=${encodeURIComponent(n)}`}export{t};
@@ -9,7 +9,7 @@
9
9
  <link rel="preconnect" href="https://fonts.googleapis.com" />
10
10
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
11
11
  <link href="https://fonts.googleapis.com/css2?family=Geist+Mono:wght@400;500;600;700&family=Geist:wght@400;500;600;700&display=swap" rel="stylesheet" />
12
- <script type="module" crossorigin src="/assets/index-DU68xk06.js"></script>
12
+ <script type="module" crossorigin src="/assets/index-CN0G8uYY.js"></script>
13
13
  <link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-FhOqtrmT.js">
14
14
  <link rel="modulepreload" crossorigin href="/assets/vendor-mermaid-BitiXJlL.js">
15
15
  <link rel="modulepreload" crossorigin href="/assets/vendor-markdown-CuWCHIVb.js">
@@ -17,14 +17,14 @@
17
17
  <link rel="modulepreload" crossorigin href="/assets/utils-Bmf_8h7Y.js">
18
18
  <link rel="modulepreload" crossorigin href="/assets/button-B9CTrsEf.js">
19
19
  <link rel="modulepreload" crossorigin href="/assets/createLucideIcon-DoiEKeJ_.js">
20
- <link rel="modulepreload" crossorigin href="/assets/api-client-C1ZwRL4Y.js">
20
+ <link rel="modulepreload" crossorigin href="/assets/api-client-Jn3njtNw.js">
21
21
  <link rel="modulepreload" crossorigin href="/assets/react-Bghu0NUO.js">
22
- <link rel="modulepreload" crossorigin href="/assets/settings-store-krC6bc79.js">
23
- <link rel="modulepreload" crossorigin href="/assets/file-store-ojgMXgAw.js">
22
+ <link rel="modulepreload" crossorigin href="/assets/settings-store-CsVSVgLp.js">
23
+ <link rel="modulepreload" crossorigin href="/assets/file-store-Ds3qJ4hC.js">
24
24
  <link rel="modulepreload" crossorigin href="/assets/window-store-UC1jJeLM.js">
25
- <link rel="modulepreload" crossorigin href="/assets/panel-store-CboGJAwh.js">
26
- <link rel="modulepreload" crossorigin href="/assets/window-pip-registry-yD6esSju.js">
27
- <link rel="modulepreload" crossorigin href="/assets/format-file-meta-C77h04cZ.js">
25
+ <link rel="modulepreload" crossorigin href="/assets/panel-store-D20Zuf3i.js">
26
+ <link rel="modulepreload" crossorigin href="/assets/window-pip-registry-D1oXq-Dq.js">
27
+ <link rel="modulepreload" crossorigin href="/assets/format-file-meta-PPi3ptkm.js">
28
28
  <link rel="modulepreload" crossorigin href="/assets/portal-container-context-BarKmT4S.js">
29
29
  <link rel="modulepreload" crossorigin href="/assets/icons-DQjqk0lJ.js">
30
30
  <link rel="modulepreload" crossorigin href="/assets/context-menu-C6pIsf83.js">
@@ -34,13 +34,13 @@
34
34
  <link rel="modulepreload" crossorigin href="/assets/mobile-bottom-sheet-C3TniEP-.js">
35
35
  <link rel="modulepreload" crossorigin href="/assets/scroll-area-BzeiOd-B.js">
36
36
  <link rel="modulepreload" crossorigin href="/assets/use-is-mobile-TkcwP1Gx.js">
37
- <link rel="modulepreload" crossorigin href="/assets/project-framework-store-B70Q3dNM.js">
38
- <link rel="modulepreload" crossorigin href="/assets/file-icons-BXdvggK2.js">
39
- <link rel="modulepreload" crossorigin href="/assets/git-repo-store-88Z8v-pF.js">
40
- <link rel="modulepreload" crossorigin href="/assets/project-store-B87hLRSk.js">
41
- <link rel="modulepreload" crossorigin href="/assets/ws-auth-CEYVQ8NH.js">
37
+ <link rel="modulepreload" crossorigin href="/assets/project-framework-store-ViZgce6y.js">
38
+ <link rel="modulepreload" crossorigin href="/assets/file-icons-wS-OmEVu.js">
39
+ <link rel="modulepreload" crossorigin href="/assets/git-repo-store-BCEPfUGT.js">
40
+ <link rel="modulepreload" crossorigin href="/assets/project-store-hNvwfqX3.js">
41
+ <link rel="modulepreload" crossorigin href="/assets/ws-auth-BG-cdrQ8.js">
42
42
  <link rel="modulepreload" crossorigin href="/assets/streaming-store-BYukdB56.js">
43
- <link rel="modulepreload" crossorigin href="/assets/tab-store-OJa1GPCq.js">
43
+ <link rel="modulepreload" crossorigin href="/assets/tab-store-krAuHQ6_.js">
44
44
  <link rel="modulepreload" crossorigin href="/assets/apply-theme-CbR31K6I.js">
45
45
  <link rel="modulepreload" crossorigin href="/assets/resolve-theme-B_ukrg_8.js">
46
46
  <link rel="stylesheet" crossorigin href="/assets/index-BH99TIE0.css">
package/dist/web/sw.js CHANGED
@@ -1 +1 @@
1
- try{self[`workbox:core:7.3.0`]&&_()}catch{}var e=(e,...t)=>{let n=e;return t.length>0&&(n+=` :: ${JSON.stringify(t)}`),n},t=class extends Error{constructor(t,n){let r=e(t,n);super(r),this.name=t,this.details=n}},n={googleAnalytics:`googleAnalytics`,precache:`precache-v2`,prefix:`workbox`,runtime:`runtime`,suffix:typeof registration<`u`?registration.scope:``},r=e=>[n.prefix,e,n.suffix].filter(e=>e&&e.length>0).join(`-`),i=e=>{for(let t of Object.keys(n))e(t)},a={updateDetails:e=>{i(t=>{typeof e[t]==`string`&&(n[t]=e[t])})},getGoogleAnalyticsName:e=>e||r(n.googleAnalytics),getPrecacheName:e=>e||r(n.precache),getPrefix:()=>n.prefix,getRuntimeName:e=>e||r(n.runtime),getSuffix:()=>n.suffix};function o(e,t){let n=t();return e.waitUntil(n),n}try{self[`workbox:precaching:7.3.0`]&&_()}catch{}var s=`__WB_REVISION__`;function c(e){if(!e)throw new t(`add-to-cache-list-unexpected-type`,{entry:e});if(typeof e==`string`){let t=new URL(e,location.href);return{cacheKey:t.href,url:t.href}}let{revision:n,url:r}=e;if(!r)throw new t(`add-to-cache-list-unexpected-type`,{entry:e});if(!n){let e=new URL(r,location.href);return{cacheKey:e.href,url:e.href}}let i=new URL(r,location.href),a=new URL(r,location.href);return i.searchParams.set(s,n),{cacheKey:i.href,url:a.href}}var l=class{constructor(){this.updatedURLs=[],this.notUpdatedURLs=[],this.handlerWillStart=async({request:e,state:t})=>{t&&(t.originalRequest=e)},this.cachedResponseWillBeUsed=async({event:e,state:t,cachedResponse:n})=>{if(e.type===`install`&&t&&t.originalRequest&&t.originalRequest instanceof Request){let e=t.originalRequest.url;n?this.notUpdatedURLs.push(e):this.updatedURLs.push(e)}return n}}},u=class{constructor({precacheController:e}){this.cacheKeyWillBeUsed=async({request:e,params:t})=>{let n=t?.cacheKey||this._precacheController.getCacheKeyForURL(e.url);return n?new Request(n,{headers:e.headers}):e},this._precacheController=e}},d;function f(){if(d===void 0){let e=new Response(``);if(`body`in e)try{new Response(e.body),d=!0}catch{d=!1}d=!1}return d}async function p(e,n){let r=null;if(e.url&&(r=new URL(e.url).origin),r!==self.location.origin)throw new t(`cross-origin-copy-response`,{origin:r});let i=e.clone(),a={headers:new Headers(i.headers),status:i.status,statusText:i.statusText},o=n?n(a):a,s=f()?i.body:await i.blob();return new Response(s,o)}var m=e=>new URL(String(e),location.href).href.replace(RegExp(`^${location.origin}`),``);function h(e,t){let n=new URL(e);for(let e of t)n.searchParams.delete(e);return n.href}async function g(e,t,n,r){let i=h(t.url,n);if(t.url===i)return e.match(t,r);let a=Object.assign(Object.assign({},r),{ignoreSearch:!0}),o=await e.keys(t,a);for(let t of o)if(i===h(t.url,n))return e.match(t,r)}var v=class{constructor(){this.promise=new Promise((e,t)=>{this.resolve=e,this.reject=t})}},y=new Set;async function b(){for(let e of y)await e()}function x(e){return new Promise(t=>setTimeout(t,e))}try{self[`workbox:strategies:7.3.0`]&&_()}catch{}function S(e){return typeof e==`string`?new Request(e):e}var C=class{constructor(e,t){this._cacheKeys={},Object.assign(this,t),this.event=t.event,this._strategy=e,this._handlerDeferred=new v,this._extendLifetimePromises=[],this._plugins=[...e.plugins],this._pluginStateMap=new Map;for(let e of this._plugins)this._pluginStateMap.set(e,{});this.event.waitUntil(this._handlerDeferred.promise)}async fetch(e){let{event:n}=this,r=S(e);if(r.mode===`navigate`&&n instanceof FetchEvent&&n.preloadResponse){let e=await n.preloadResponse;if(e)return e}let i=this.hasCallback(`fetchDidFail`)?r.clone():null;try{for(let e of this.iterateCallbacks(`requestWillFetch`))r=await e({request:r.clone(),event:n})}catch(e){if(e instanceof Error)throw new t(`plugin-error-request-will-fetch`,{thrownErrorMessage:e.message})}let a=r.clone();try{let e;e=await fetch(r,r.mode===`navigate`?void 0:this._strategy.fetchOptions);for(let t of this.iterateCallbacks(`fetchDidSucceed`))e=await t({event:n,request:a,response:e});return e}catch(e){throw i&&await this.runCallbacks(`fetchDidFail`,{error:e,event:n,originalRequest:i.clone(),request:a.clone()}),e}}async fetchAndCachePut(e){let t=await this.fetch(e),n=t.clone();return this.waitUntil(this.cachePut(e,n)),t}async cacheMatch(e){let t=S(e),n,{cacheName:r,matchOptions:i}=this._strategy,a=await this.getCacheKey(t,`read`),o=Object.assign(Object.assign({},i),{cacheName:r});n=await caches.match(a,o);for(let e of this.iterateCallbacks(`cachedResponseWillBeUsed`))n=await e({cacheName:r,matchOptions:i,cachedResponse:n,request:a,event:this.event})||void 0;return n}async cachePut(e,n){let r=S(e);await x(0);let i=await this.getCacheKey(r,`write`);if(!n)throw new t(`cache-put-with-no-response`,{url:m(i.url)});let a=await this._ensureResponseSafeToCache(n);if(!a)return!1;let{cacheName:o,matchOptions:s}=this._strategy,c=await self.caches.open(o),l=this.hasCallback(`cacheDidUpdate`),u=l?await g(c,i.clone(),[`__WB_REVISION__`],s):null;try{await c.put(i,l?a.clone():a)}catch(e){if(e instanceof Error)throw e.name===`QuotaExceededError`&&await b(),e}for(let e of this.iterateCallbacks(`cacheDidUpdate`))await e({cacheName:o,oldResponse:u,newResponse:a.clone(),request:i,event:this.event});return!0}async getCacheKey(e,t){let n=`${e.url} | ${t}`;if(!this._cacheKeys[n]){let r=e;for(let e of this.iterateCallbacks(`cacheKeyWillBeUsed`))r=S(await e({mode:t,request:r,event:this.event,params:this.params}));this._cacheKeys[n]=r}return this._cacheKeys[n]}hasCallback(e){for(let t of this._strategy.plugins)if(e in t)return!0;return!1}async runCallbacks(e,t){for(let n of this.iterateCallbacks(e))await n(t)}*iterateCallbacks(e){for(let t of this._strategy.plugins)if(typeof t[e]==`function`){let n=this._pluginStateMap.get(t);yield r=>{let i=Object.assign(Object.assign({},r),{state:n});return t[e](i)}}}waitUntil(e){return this._extendLifetimePromises.push(e),e}async doneWaiting(){for(;this._extendLifetimePromises.length;){let e=this._extendLifetimePromises.splice(0),t=(await Promise.allSettled(e)).find(e=>e.status===`rejected`);if(t)throw t.reason}}destroy(){this._handlerDeferred.resolve(null)}async _ensureResponseSafeToCache(e){let t=e,n=!1;for(let e of this.iterateCallbacks(`cacheWillUpdate`))if(t=await e({request:this.request,response:t,event:this.event})||void 0,n=!0,!t)break;return n||t&&t.status!==200&&(t=void 0),t}},w=class{constructor(e={}){this.cacheName=a.getRuntimeName(e.cacheName),this.plugins=e.plugins||[],this.fetchOptions=e.fetchOptions,this.matchOptions=e.matchOptions}handle(e){let[t]=this.handleAll(e);return t}handleAll(e){e instanceof FetchEvent&&(e={event:e,request:e.request});let t=e.event,n=typeof e.request==`string`?new Request(e.request):e.request,r=`params`in e?e.params:void 0,i=new C(this,{event:t,request:n,params:r}),a=this._getResponse(i,n,t);return[a,this._awaitComplete(a,i,n,t)]}async _getResponse(e,n,r){await e.runCallbacks(`handlerWillStart`,{event:r,request:n});let i;try{if(i=await this._handle(n,e),!i||i.type===`error`)throw new t(`no-response`,{url:n.url})}catch(t){if(t instanceof Error){for(let a of e.iterateCallbacks(`handlerDidError`))if(i=await a({error:t,event:r,request:n}),i)break}if(!i)throw t}for(let t of e.iterateCallbacks(`handlerWillRespond`))i=await t({event:r,request:n,response:i});return i}async _awaitComplete(e,t,n,r){let i,a;try{i=await e}catch{}try{await t.runCallbacks(`handlerDidRespond`,{event:r,request:n,response:i}),await t.doneWaiting()}catch(e){e instanceof Error&&(a=e)}if(await t.runCallbacks(`handlerDidComplete`,{event:r,request:n,response:i,error:a}),t.destroy(),a)throw a}},T=class e extends w{constructor(t={}){t.cacheName=a.getPrecacheName(t.cacheName),super(t),this._fallbackToNetwork=t.fallbackToNetwork!==!1,this.plugins.push(e.copyRedirectedCacheableResponsesPlugin)}async _handle(e,t){return await t.cacheMatch(e)||(t.event&&t.event.type===`install`?await this._handleInstall(e,t):await this._handleFetch(e,t))}async _handleFetch(e,n){let r,i=n.params||{};if(this._fallbackToNetwork){let t=i.integrity,a=e.integrity,o=!a||a===t;r=await n.fetch(new Request(e,{integrity:e.mode===`no-cors`?void 0:a||t})),t&&o&&e.mode!==`no-cors`&&(this._useDefaultCacheabilityPluginIfNeeded(),await n.cachePut(e,r.clone()))}else throw new t(`missing-precache-entry`,{cacheName:this.cacheName,url:e.url});return r}async _handleInstall(e,n){this._useDefaultCacheabilityPluginIfNeeded();let r=await n.fetch(e);if(!await n.cachePut(e,r.clone()))throw new t(`bad-precaching-response`,{url:e.url,status:r.status});return r}_useDefaultCacheabilityPluginIfNeeded(){let t=null,n=0;for(let[r,i]of this.plugins.entries())i!==e.copyRedirectedCacheableResponsesPlugin&&(i===e.defaultPrecacheCacheabilityPlugin&&(t=r),i.cacheWillUpdate&&n++);n===0?this.plugins.push(e.defaultPrecacheCacheabilityPlugin):n>1&&t!==null&&this.plugins.splice(t,1)}};T.defaultPrecacheCacheabilityPlugin={async cacheWillUpdate({response:e}){return!e||e.status>=400?null:e}},T.copyRedirectedCacheableResponsesPlugin={async cacheWillUpdate({response:e}){return e.redirected?await p(e):e}};var E=class{constructor({cacheName:e,plugins:t=[],fallbackToNetwork:n=!0}={}){this._urlsToCacheKeys=new Map,this._urlsToCacheModes=new Map,this._cacheKeysToIntegrities=new Map,this._strategy=new T({cacheName:a.getPrecacheName(e),plugins:[...t,new u({precacheController:this})],fallbackToNetwork:n}),this.install=this.install.bind(this),this.activate=this.activate.bind(this)}get strategy(){return this._strategy}precache(e){this.addToCacheList(e),this._installAndActiveListenersAdded||=(self.addEventListener(`install`,this.install),self.addEventListener(`activate`,this.activate),!0)}addToCacheList(e){let n=[];for(let r of e){typeof r==`string`?n.push(r):r&&r.revision===void 0&&n.push(r.url);let{cacheKey:e,url:i}=c(r),a=typeof r!=`string`&&r.revision?`reload`:`default`;if(this._urlsToCacheKeys.has(i)&&this._urlsToCacheKeys.get(i)!==e)throw new t(`add-to-cache-list-conflicting-entries`,{firstEntry:this._urlsToCacheKeys.get(i),secondEntry:e});if(typeof r!=`string`&&r.integrity){if(this._cacheKeysToIntegrities.has(e)&&this._cacheKeysToIntegrities.get(e)!==r.integrity)throw new t(`add-to-cache-list-conflicting-integrities`,{url:i});this._cacheKeysToIntegrities.set(e,r.integrity)}if(this._urlsToCacheKeys.set(i,e),this._urlsToCacheModes.set(i,a),n.length>0){let e=`Workbox is precaching URLs without revision info: ${n.join(`, `)}\nThis is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(e)}}}install(e){return o(e,async()=>{let t=new l;this.strategy.plugins.push(t);for(let[t,n]of this._urlsToCacheKeys){let r=this._cacheKeysToIntegrities.get(n),i=this._urlsToCacheModes.get(t),a=new Request(t,{integrity:r,cache:i,credentials:`same-origin`});await Promise.all(this.strategy.handleAll({params:{cacheKey:n},request:a,event:e}))}let{updatedURLs:n,notUpdatedURLs:r}=t;return{updatedURLs:n,notUpdatedURLs:r}})}activate(e){return o(e,async()=>{let e=await self.caches.open(this.strategy.cacheName),t=await e.keys(),n=new Set(this._urlsToCacheKeys.values()),r=[];for(let i of t)n.has(i.url)||(await e.delete(i),r.push(i.url));return{deletedURLs:r}})}getURLsToCacheKeys(){return this._urlsToCacheKeys}getCachedURLs(){return[...this._urlsToCacheKeys.keys()]}getCacheKeyForURL(e){let t=new URL(e,location.href);return this._urlsToCacheKeys.get(t.href)}getIntegrityForCacheKey(e){return this._cacheKeysToIntegrities.get(e)}async matchPrecache(e){let t=e instanceof Request?e.url:e,n=this.getCacheKeyForURL(t);if(n)return(await self.caches.open(this.strategy.cacheName)).match(n)}createHandlerBoundToURL(e){let n=this.getCacheKeyForURL(e);if(!n)throw new t(`non-precached-url`,{url:e});return t=>(t.request=new Request(e),t.params=Object.assign({cacheKey:n},t.params),this.strategy.handle(t))}},D,O=()=>(D||=new E,D);try{self[`workbox:routing:7.3.0`]&&_()}catch{}var k=e=>e&&typeof e==`object`?e:{handle:e},A=class{constructor(e,t,n=`GET`){this.handler=k(t),this.match=e,this.method=n}setCatchHandler(e){this.catchHandler=k(e)}},j=class extends A{constructor(e,t,n){super(({url:t})=>{let n=e.exec(t.href);if(n&&!(t.origin!==location.origin&&n.index!==0))return n.slice(1)},t,n)}},M=class{constructor(){this._routes=new Map,this._defaultHandlerMap=new Map}get routes(){return this._routes}addFetchListener(){self.addEventListener(`fetch`,(e=>{let{request:t}=e,n=this.handleRequest({request:t,event:e});n&&e.respondWith(n)}))}addCacheListener(){self.addEventListener(`message`,(e=>{if(e.data&&e.data.type===`CACHE_URLS`){let{payload:t}=e.data,n=Promise.all(t.urlsToCache.map(t=>{typeof t==`string`&&(t=[t]);let n=new Request(...t);return this.handleRequest({request:n,event:e})}));e.waitUntil(n),e.ports&&e.ports[0]&&n.then(()=>e.ports[0].postMessage(!0))}}))}handleRequest({request:e,event:t}){let n=new URL(e.url,location.href);if(!n.protocol.startsWith(`http`))return;let r=n.origin===location.origin,{params:i,route:a}=this.findMatchingRoute({event:t,request:e,sameOrigin:r,url:n}),o=a&&a.handler,s=e.method;if(!o&&this._defaultHandlerMap.has(s)&&(o=this._defaultHandlerMap.get(s)),!o)return;let c;try{c=o.handle({url:n,request:e,event:t,params:i})}catch(e){c=Promise.reject(e)}let l=a&&a.catchHandler;return c instanceof Promise&&(this._catchHandler||l)&&(c=c.catch(async r=>{if(l)try{return await l.handle({url:n,request:e,event:t,params:i})}catch(e){e instanceof Error&&(r=e)}if(this._catchHandler)return this._catchHandler.handle({url:n,request:e,event:t});throw r})),c}findMatchingRoute({url:e,sameOrigin:t,request:n,event:r}){let i=this._routes.get(n.method)||[];for(let a of i){let i,o=a.match({url:e,sameOrigin:t,request:n,event:r});if(o)return i=o,(Array.isArray(i)&&i.length===0||o.constructor===Object&&Object.keys(o).length===0||typeof o==`boolean`)&&(i=void 0),{route:a,params:i}}return{}}setDefaultHandler(e,t=`GET`){this._defaultHandlerMap.set(t,k(e))}setCatchHandler(e){this._catchHandler=k(e)}registerRoute(e){this._routes.has(e.method)||this._routes.set(e.method,[]),this._routes.get(e.method).push(e)}unregisterRoute(e){if(!this._routes.has(e.method))throw new t(`unregister-route-but-not-found-with-method`,{method:e.method});let n=this._routes.get(e.method).indexOf(e);if(n>-1)this._routes.get(e.method).splice(n,1);else throw new t(`unregister-route-route-not-registered`)}},N,P=()=>(N||(N=new M,N.addFetchListener(),N.addCacheListener()),N);function F(e,n,r){let i;if(typeof e==`string`){let t=new URL(e,location.href);i=new A(({url:e})=>e.href===t.href,n,r)}else if(e instanceof RegExp)i=new j(e,n,r);else if(typeof e==`function`)i=new A(e,n,r);else if(e instanceof A)i=e;else throw new t(`unsupported-route-type`,{moduleName:`workbox-routing`,funcName:`registerRoute`,paramName:`capture`});return P().registerRoute(i),i}function I(e,t=[]){for(let n of[...e.searchParams.keys()])t.some(e=>e.test(n))&&e.searchParams.delete(n);return e}function*L(e,{ignoreURLParametersMatching:t=[/^utm_/,/^fbclid$/],directoryIndex:n=`index.html`,cleanURLs:r=!0,urlManipulation:i}={}){let a=new URL(e,location.href);a.hash=``,yield a.href;let o=I(a,t);if(yield o.href,n&&o.pathname.endsWith(`/`)){let e=new URL(o.href);e.pathname+=n,yield e.href}if(r){let e=new URL(o.href);e.pathname+=`.html`,yield e.href}if(i){let e=i({url:a});for(let t of e)yield t.href}}var R=class extends A{constructor(e,t){super(({request:n})=>{let r=e.getURLsToCacheKeys();for(let i of L(n.url,t)){let t=r.get(i);if(t)return{cacheKey:t,integrity:e.getIntegrityForCacheKey(t)}}},e.strategy)}};function z(e){F(new R(O(),e))}var B=`-precache-`,V=async(e,t=B)=>{let n=(await self.caches.keys()).filter(n=>n.includes(t)&&n.includes(self.registration.scope)&&n!==e);return await Promise.all(n.map(e=>self.caches.delete(e))),n};function H(){self.addEventListener(`activate`,(e=>{let t=a.getPrecacheName();e.waitUntil(V(t).then(e=>{}))}))}function U(e){O().precache(e)}function W(e,t){U(e),z(t)}var G=class extends w{async _handle(e,n){let r=await n.cacheMatch(e),i;if(!r)try{r=await n.fetchAndCachePut(e)}catch(e){e instanceof Error&&(i=e)}if(!r)throw new t(`no-response`,{url:e.url,error:i});return r}};W([{"revision":"166e49a6feb053ee22b5265ea5ab7a0d","url":"index.html"},{"revision":"26dccd02a2ef7522892015154f5e3680","url":"manifest.webmanifest"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-512.svg"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-192.svg"},{"revision":null,"url":"assets/index-DU68xk06.js"},{"revision":null,"url":"assets/index-BH99TIE0.css"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-192.svg"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-512.svg"},{"revision":"26dccd02a2ef7522892015154f5e3680","url":"manifest.webmanifest"}]),H();var K=e=>(e.headers.get(`content-type`)??``).startsWith(`text/html`);F(({url:e})=>e.pathname.startsWith(`/assets/`),new G({cacheName:`ppm-assets`,plugins:[{cacheWillUpdate:async({response:e})=>K(e)?null:e,cachedResponseWillBeUsed:async({cachedResponse:e})=>e&&K(e)?null:e}]})),self.skipWaiting(),self.addEventListener(`activate`,()=>{self.clients.claim()});
1
+ try{self[`workbox:core:7.3.0`]&&_()}catch{}var e=(e,...t)=>{let n=e;return t.length>0&&(n+=` :: ${JSON.stringify(t)}`),n},t=class extends Error{constructor(t,n){let r=e(t,n);super(r),this.name=t,this.details=n}},n={googleAnalytics:`googleAnalytics`,precache:`precache-v2`,prefix:`workbox`,runtime:`runtime`,suffix:typeof registration<`u`?registration.scope:``},r=e=>[n.prefix,e,n.suffix].filter(e=>e&&e.length>0).join(`-`),i=e=>{for(let t of Object.keys(n))e(t)},a={updateDetails:e=>{i(t=>{typeof e[t]==`string`&&(n[t]=e[t])})},getGoogleAnalyticsName:e=>e||r(n.googleAnalytics),getPrecacheName:e=>e||r(n.precache),getPrefix:()=>n.prefix,getRuntimeName:e=>e||r(n.runtime),getSuffix:()=>n.suffix};function o(e,t){let n=t();return e.waitUntil(n),n}try{self[`workbox:precaching:7.3.0`]&&_()}catch{}var s=`__WB_REVISION__`;function c(e){if(!e)throw new t(`add-to-cache-list-unexpected-type`,{entry:e});if(typeof e==`string`){let t=new URL(e,location.href);return{cacheKey:t.href,url:t.href}}let{revision:n,url:r}=e;if(!r)throw new t(`add-to-cache-list-unexpected-type`,{entry:e});if(!n){let e=new URL(r,location.href);return{cacheKey:e.href,url:e.href}}let i=new URL(r,location.href),a=new URL(r,location.href);return i.searchParams.set(s,n),{cacheKey:i.href,url:a.href}}var l=class{constructor(){this.updatedURLs=[],this.notUpdatedURLs=[],this.handlerWillStart=async({request:e,state:t})=>{t&&(t.originalRequest=e)},this.cachedResponseWillBeUsed=async({event:e,state:t,cachedResponse:n})=>{if(e.type===`install`&&t&&t.originalRequest&&t.originalRequest instanceof Request){let e=t.originalRequest.url;n?this.notUpdatedURLs.push(e):this.updatedURLs.push(e)}return n}}},u=class{constructor({precacheController:e}){this.cacheKeyWillBeUsed=async({request:e,params:t})=>{let n=t?.cacheKey||this._precacheController.getCacheKeyForURL(e.url);return n?new Request(n,{headers:e.headers}):e},this._precacheController=e}},d;function f(){if(d===void 0){let e=new Response(``);if(`body`in e)try{new Response(e.body),d=!0}catch{d=!1}d=!1}return d}async function p(e,n){let r=null;if(e.url&&(r=new URL(e.url).origin),r!==self.location.origin)throw new t(`cross-origin-copy-response`,{origin:r});let i=e.clone(),a={headers:new Headers(i.headers),status:i.status,statusText:i.statusText},o=n?n(a):a,s=f()?i.body:await i.blob();return new Response(s,o)}var m=e=>new URL(String(e),location.href).href.replace(RegExp(`^${location.origin}`),``);function h(e,t){let n=new URL(e);for(let e of t)n.searchParams.delete(e);return n.href}async function g(e,t,n,r){let i=h(t.url,n);if(t.url===i)return e.match(t,r);let a=Object.assign(Object.assign({},r),{ignoreSearch:!0}),o=await e.keys(t,a);for(let t of o)if(i===h(t.url,n))return e.match(t,r)}var v=class{constructor(){this.promise=new Promise((e,t)=>{this.resolve=e,this.reject=t})}},y=new Set;async function b(){for(let e of y)await e()}function x(e){return new Promise(t=>setTimeout(t,e))}try{self[`workbox:strategies:7.3.0`]&&_()}catch{}function S(e){return typeof e==`string`?new Request(e):e}var C=class{constructor(e,t){this._cacheKeys={},Object.assign(this,t),this.event=t.event,this._strategy=e,this._handlerDeferred=new v,this._extendLifetimePromises=[],this._plugins=[...e.plugins],this._pluginStateMap=new Map;for(let e of this._plugins)this._pluginStateMap.set(e,{});this.event.waitUntil(this._handlerDeferred.promise)}async fetch(e){let{event:n}=this,r=S(e);if(r.mode===`navigate`&&n instanceof FetchEvent&&n.preloadResponse){let e=await n.preloadResponse;if(e)return e}let i=this.hasCallback(`fetchDidFail`)?r.clone():null;try{for(let e of this.iterateCallbacks(`requestWillFetch`))r=await e({request:r.clone(),event:n})}catch(e){if(e instanceof Error)throw new t(`plugin-error-request-will-fetch`,{thrownErrorMessage:e.message})}let a=r.clone();try{let e;e=await fetch(r,r.mode===`navigate`?void 0:this._strategy.fetchOptions);for(let t of this.iterateCallbacks(`fetchDidSucceed`))e=await t({event:n,request:a,response:e});return e}catch(e){throw i&&await this.runCallbacks(`fetchDidFail`,{error:e,event:n,originalRequest:i.clone(),request:a.clone()}),e}}async fetchAndCachePut(e){let t=await this.fetch(e),n=t.clone();return this.waitUntil(this.cachePut(e,n)),t}async cacheMatch(e){let t=S(e),n,{cacheName:r,matchOptions:i}=this._strategy,a=await this.getCacheKey(t,`read`),o=Object.assign(Object.assign({},i),{cacheName:r});n=await caches.match(a,o);for(let e of this.iterateCallbacks(`cachedResponseWillBeUsed`))n=await e({cacheName:r,matchOptions:i,cachedResponse:n,request:a,event:this.event})||void 0;return n}async cachePut(e,n){let r=S(e);await x(0);let i=await this.getCacheKey(r,`write`);if(!n)throw new t(`cache-put-with-no-response`,{url:m(i.url)});let a=await this._ensureResponseSafeToCache(n);if(!a)return!1;let{cacheName:o,matchOptions:s}=this._strategy,c=await self.caches.open(o),l=this.hasCallback(`cacheDidUpdate`),u=l?await g(c,i.clone(),[`__WB_REVISION__`],s):null;try{await c.put(i,l?a.clone():a)}catch(e){if(e instanceof Error)throw e.name===`QuotaExceededError`&&await b(),e}for(let e of this.iterateCallbacks(`cacheDidUpdate`))await e({cacheName:o,oldResponse:u,newResponse:a.clone(),request:i,event:this.event});return!0}async getCacheKey(e,t){let n=`${e.url} | ${t}`;if(!this._cacheKeys[n]){let r=e;for(let e of this.iterateCallbacks(`cacheKeyWillBeUsed`))r=S(await e({mode:t,request:r,event:this.event,params:this.params}));this._cacheKeys[n]=r}return this._cacheKeys[n]}hasCallback(e){for(let t of this._strategy.plugins)if(e in t)return!0;return!1}async runCallbacks(e,t){for(let n of this.iterateCallbacks(e))await n(t)}*iterateCallbacks(e){for(let t of this._strategy.plugins)if(typeof t[e]==`function`){let n=this._pluginStateMap.get(t);yield r=>{let i=Object.assign(Object.assign({},r),{state:n});return t[e](i)}}}waitUntil(e){return this._extendLifetimePromises.push(e),e}async doneWaiting(){for(;this._extendLifetimePromises.length;){let e=this._extendLifetimePromises.splice(0),t=(await Promise.allSettled(e)).find(e=>e.status===`rejected`);if(t)throw t.reason}}destroy(){this._handlerDeferred.resolve(null)}async _ensureResponseSafeToCache(e){let t=e,n=!1;for(let e of this.iterateCallbacks(`cacheWillUpdate`))if(t=await e({request:this.request,response:t,event:this.event})||void 0,n=!0,!t)break;return n||t&&t.status!==200&&(t=void 0),t}},w=class{constructor(e={}){this.cacheName=a.getRuntimeName(e.cacheName),this.plugins=e.plugins||[],this.fetchOptions=e.fetchOptions,this.matchOptions=e.matchOptions}handle(e){let[t]=this.handleAll(e);return t}handleAll(e){e instanceof FetchEvent&&(e={event:e,request:e.request});let t=e.event,n=typeof e.request==`string`?new Request(e.request):e.request,r=`params`in e?e.params:void 0,i=new C(this,{event:t,request:n,params:r}),a=this._getResponse(i,n,t);return[a,this._awaitComplete(a,i,n,t)]}async _getResponse(e,n,r){await e.runCallbacks(`handlerWillStart`,{event:r,request:n});let i;try{if(i=await this._handle(n,e),!i||i.type===`error`)throw new t(`no-response`,{url:n.url})}catch(t){if(t instanceof Error){for(let a of e.iterateCallbacks(`handlerDidError`))if(i=await a({error:t,event:r,request:n}),i)break}if(!i)throw t}for(let t of e.iterateCallbacks(`handlerWillRespond`))i=await t({event:r,request:n,response:i});return i}async _awaitComplete(e,t,n,r){let i,a;try{i=await e}catch{}try{await t.runCallbacks(`handlerDidRespond`,{event:r,request:n,response:i}),await t.doneWaiting()}catch(e){e instanceof Error&&(a=e)}if(await t.runCallbacks(`handlerDidComplete`,{event:r,request:n,response:i,error:a}),t.destroy(),a)throw a}},T=class e extends w{constructor(t={}){t.cacheName=a.getPrecacheName(t.cacheName),super(t),this._fallbackToNetwork=t.fallbackToNetwork!==!1,this.plugins.push(e.copyRedirectedCacheableResponsesPlugin)}async _handle(e,t){return await t.cacheMatch(e)||(t.event&&t.event.type===`install`?await this._handleInstall(e,t):await this._handleFetch(e,t))}async _handleFetch(e,n){let r,i=n.params||{};if(this._fallbackToNetwork){let t=i.integrity,a=e.integrity,o=!a||a===t;r=await n.fetch(new Request(e,{integrity:e.mode===`no-cors`?void 0:a||t})),t&&o&&e.mode!==`no-cors`&&(this._useDefaultCacheabilityPluginIfNeeded(),await n.cachePut(e,r.clone()))}else throw new t(`missing-precache-entry`,{cacheName:this.cacheName,url:e.url});return r}async _handleInstall(e,n){this._useDefaultCacheabilityPluginIfNeeded();let r=await n.fetch(e);if(!await n.cachePut(e,r.clone()))throw new t(`bad-precaching-response`,{url:e.url,status:r.status});return r}_useDefaultCacheabilityPluginIfNeeded(){let t=null,n=0;for(let[r,i]of this.plugins.entries())i!==e.copyRedirectedCacheableResponsesPlugin&&(i===e.defaultPrecacheCacheabilityPlugin&&(t=r),i.cacheWillUpdate&&n++);n===0?this.plugins.push(e.defaultPrecacheCacheabilityPlugin):n>1&&t!==null&&this.plugins.splice(t,1)}};T.defaultPrecacheCacheabilityPlugin={async cacheWillUpdate({response:e}){return!e||e.status>=400?null:e}},T.copyRedirectedCacheableResponsesPlugin={async cacheWillUpdate({response:e}){return e.redirected?await p(e):e}};var E=class{constructor({cacheName:e,plugins:t=[],fallbackToNetwork:n=!0}={}){this._urlsToCacheKeys=new Map,this._urlsToCacheModes=new Map,this._cacheKeysToIntegrities=new Map,this._strategy=new T({cacheName:a.getPrecacheName(e),plugins:[...t,new u({precacheController:this})],fallbackToNetwork:n}),this.install=this.install.bind(this),this.activate=this.activate.bind(this)}get strategy(){return this._strategy}precache(e){this.addToCacheList(e),this._installAndActiveListenersAdded||=(self.addEventListener(`install`,this.install),self.addEventListener(`activate`,this.activate),!0)}addToCacheList(e){let n=[];for(let r of e){typeof r==`string`?n.push(r):r&&r.revision===void 0&&n.push(r.url);let{cacheKey:e,url:i}=c(r),a=typeof r!=`string`&&r.revision?`reload`:`default`;if(this._urlsToCacheKeys.has(i)&&this._urlsToCacheKeys.get(i)!==e)throw new t(`add-to-cache-list-conflicting-entries`,{firstEntry:this._urlsToCacheKeys.get(i),secondEntry:e});if(typeof r!=`string`&&r.integrity){if(this._cacheKeysToIntegrities.has(e)&&this._cacheKeysToIntegrities.get(e)!==r.integrity)throw new t(`add-to-cache-list-conflicting-integrities`,{url:i});this._cacheKeysToIntegrities.set(e,r.integrity)}if(this._urlsToCacheKeys.set(i,e),this._urlsToCacheModes.set(i,a),n.length>0){let e=`Workbox is precaching URLs without revision info: ${n.join(`, `)}\nThis is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(e)}}}install(e){return o(e,async()=>{let t=new l;this.strategy.plugins.push(t);for(let[t,n]of this._urlsToCacheKeys){let r=this._cacheKeysToIntegrities.get(n),i=this._urlsToCacheModes.get(t),a=new Request(t,{integrity:r,cache:i,credentials:`same-origin`});await Promise.all(this.strategy.handleAll({params:{cacheKey:n},request:a,event:e}))}let{updatedURLs:n,notUpdatedURLs:r}=t;return{updatedURLs:n,notUpdatedURLs:r}})}activate(e){return o(e,async()=>{let e=await self.caches.open(this.strategy.cacheName),t=await e.keys(),n=new Set(this._urlsToCacheKeys.values()),r=[];for(let i of t)n.has(i.url)||(await e.delete(i),r.push(i.url));return{deletedURLs:r}})}getURLsToCacheKeys(){return this._urlsToCacheKeys}getCachedURLs(){return[...this._urlsToCacheKeys.keys()]}getCacheKeyForURL(e){let t=new URL(e,location.href);return this._urlsToCacheKeys.get(t.href)}getIntegrityForCacheKey(e){return this._cacheKeysToIntegrities.get(e)}async matchPrecache(e){let t=e instanceof Request?e.url:e,n=this.getCacheKeyForURL(t);if(n)return(await self.caches.open(this.strategy.cacheName)).match(n)}createHandlerBoundToURL(e){let n=this.getCacheKeyForURL(e);if(!n)throw new t(`non-precached-url`,{url:e});return t=>(t.request=new Request(e),t.params=Object.assign({cacheKey:n},t.params),this.strategy.handle(t))}},D,O=()=>(D||=new E,D);try{self[`workbox:routing:7.3.0`]&&_()}catch{}var k=e=>e&&typeof e==`object`?e:{handle:e},A=class{constructor(e,t,n=`GET`){this.handler=k(t),this.match=e,this.method=n}setCatchHandler(e){this.catchHandler=k(e)}},j=class extends A{constructor(e,t,n){super(({url:t})=>{let n=e.exec(t.href);if(n&&!(t.origin!==location.origin&&n.index!==0))return n.slice(1)},t,n)}},M=class{constructor(){this._routes=new Map,this._defaultHandlerMap=new Map}get routes(){return this._routes}addFetchListener(){self.addEventListener(`fetch`,(e=>{let{request:t}=e,n=this.handleRequest({request:t,event:e});n&&e.respondWith(n)}))}addCacheListener(){self.addEventListener(`message`,(e=>{if(e.data&&e.data.type===`CACHE_URLS`){let{payload:t}=e.data,n=Promise.all(t.urlsToCache.map(t=>{typeof t==`string`&&(t=[t]);let n=new Request(...t);return this.handleRequest({request:n,event:e})}));e.waitUntil(n),e.ports&&e.ports[0]&&n.then(()=>e.ports[0].postMessage(!0))}}))}handleRequest({request:e,event:t}){let n=new URL(e.url,location.href);if(!n.protocol.startsWith(`http`))return;let r=n.origin===location.origin,{params:i,route:a}=this.findMatchingRoute({event:t,request:e,sameOrigin:r,url:n}),o=a&&a.handler,s=e.method;if(!o&&this._defaultHandlerMap.has(s)&&(o=this._defaultHandlerMap.get(s)),!o)return;let c;try{c=o.handle({url:n,request:e,event:t,params:i})}catch(e){c=Promise.reject(e)}let l=a&&a.catchHandler;return c instanceof Promise&&(this._catchHandler||l)&&(c=c.catch(async r=>{if(l)try{return await l.handle({url:n,request:e,event:t,params:i})}catch(e){e instanceof Error&&(r=e)}if(this._catchHandler)return this._catchHandler.handle({url:n,request:e,event:t});throw r})),c}findMatchingRoute({url:e,sameOrigin:t,request:n,event:r}){let i=this._routes.get(n.method)||[];for(let a of i){let i,o=a.match({url:e,sameOrigin:t,request:n,event:r});if(o)return i=o,(Array.isArray(i)&&i.length===0||o.constructor===Object&&Object.keys(o).length===0||typeof o==`boolean`)&&(i=void 0),{route:a,params:i}}return{}}setDefaultHandler(e,t=`GET`){this._defaultHandlerMap.set(t,k(e))}setCatchHandler(e){this._catchHandler=k(e)}registerRoute(e){this._routes.has(e.method)||this._routes.set(e.method,[]),this._routes.get(e.method).push(e)}unregisterRoute(e){if(!this._routes.has(e.method))throw new t(`unregister-route-but-not-found-with-method`,{method:e.method});let n=this._routes.get(e.method).indexOf(e);if(n>-1)this._routes.get(e.method).splice(n,1);else throw new t(`unregister-route-route-not-registered`)}},N,P=()=>(N||(N=new M,N.addFetchListener(),N.addCacheListener()),N);function F(e,n,r){let i;if(typeof e==`string`){let t=new URL(e,location.href);i=new A(({url:e})=>e.href===t.href,n,r)}else if(e instanceof RegExp)i=new j(e,n,r);else if(typeof e==`function`)i=new A(e,n,r);else if(e instanceof A)i=e;else throw new t(`unsupported-route-type`,{moduleName:`workbox-routing`,funcName:`registerRoute`,paramName:`capture`});return P().registerRoute(i),i}function I(e,t=[]){for(let n of[...e.searchParams.keys()])t.some(e=>e.test(n))&&e.searchParams.delete(n);return e}function*L(e,{ignoreURLParametersMatching:t=[/^utm_/,/^fbclid$/],directoryIndex:n=`index.html`,cleanURLs:r=!0,urlManipulation:i}={}){let a=new URL(e,location.href);a.hash=``,yield a.href;let o=I(a,t);if(yield o.href,n&&o.pathname.endsWith(`/`)){let e=new URL(o.href);e.pathname+=n,yield e.href}if(r){let e=new URL(o.href);e.pathname+=`.html`,yield e.href}if(i){let e=i({url:a});for(let t of e)yield t.href}}var R=class extends A{constructor(e,t){super(({request:n})=>{let r=e.getURLsToCacheKeys();for(let i of L(n.url,t)){let t=r.get(i);if(t)return{cacheKey:t,integrity:e.getIntegrityForCacheKey(t)}}},e.strategy)}};function z(e){F(new R(O(),e))}var B=`-precache-`,V=async(e,t=B)=>{let n=(await self.caches.keys()).filter(n=>n.includes(t)&&n.includes(self.registration.scope)&&n!==e);return await Promise.all(n.map(e=>self.caches.delete(e))),n};function H(){self.addEventListener(`activate`,(e=>{let t=a.getPrecacheName();e.waitUntil(V(t).then(e=>{}))}))}function U(e){O().precache(e)}function W(e,t){U(e),z(t)}var G=class extends w{async _handle(e,n){let r=await n.cacheMatch(e),i;if(!r)try{r=await n.fetchAndCachePut(e)}catch(e){e instanceof Error&&(i=e)}if(!r)throw new t(`no-response`,{url:e.url,error:i});return r}};W([{"revision":"206312c0035b4551edbf7f8f5cf4d80f","url":"index.html"},{"revision":"26dccd02a2ef7522892015154f5e3680","url":"manifest.webmanifest"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-512.svg"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-192.svg"},{"revision":null,"url":"assets/index-CN0G8uYY.js"},{"revision":null,"url":"assets/index-BH99TIE0.css"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-192.svg"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-512.svg"},{"revision":"26dccd02a2ef7522892015154f5e3680","url":"manifest.webmanifest"}]),H();var K=e=>(e.headers.get(`content-type`)??``).startsWith(`text/html`);F(({url:e})=>e.pathname.startsWith(`/assets/`),new G({cacheName:`ppm-assets`,plugins:[{cacheWillUpdate:async({response:e})=>K(e)?null:e,cachedResponseWillBeUsed:async({cachedResponse:e})=>e&&K(e)?null:e}]})),self.skipWaiting(),self.addEventListener(`activate`,()=>{self.clients.claim()});
@@ -2,6 +2,15 @@
2
2
 
3
3
  > Part of the [PPM system architecture](../system-architecture.md).
4
4
 
5
+ ## Codex daily guard for weekly-only accounts
6
+
7
+ Daily guard spreads a seven-day quota window across five weekdays: each weekday
8
+ unlocks another 20% of the weekly quota. Unused allowance carries forward.
9
+ Saturday/Sunday slots keep the previous cap; a window starting on a weekend
10
+ opens its first allowance on Monday. Slots are 24 hours from the weekly reset
11
+ time and use UTC weekdays, consistently in the server and browser. This does not
12
+ change Codex's reset schedule. Users can disable the guard per account.
13
+
5
14
  ## Codex context settings
6
15
 
7
16
  **AI Settings → Codex** exposes **Context window (tokens)** and **Auto-compact
@@ -2,6 +2,19 @@
2
2
 
3
3
  > Part of the [PPM system architecture](../system-architecture.md).
4
4
 
5
+ ## New Chat Provider
6
+
7
+ Settings → AI offers `Always use default provider` and `Follow last focused chat`.
8
+ `ai.default_provider` is the fixed choice or the fallback when the current project has
9
+ no focused chat. Fresh installations use follow-focus; existing configurations without
10
+ `ai.new_chat_provider_mode` retain default mode.
11
+
12
+ Focus memory is per project and browser session, across panels. Editors, terminals and
13
+ panel chrome do not replace it. New tabs snapshot the source at creation and resolve
14
+ settings before mounting chat or claiming an account. An unavailable provider prompts
15
+ for an available choice. Existing tabs, resumed sessions and explicit fork/clear
16
+ providers are preserved; changing the setting only affects new tabs.
17
+
5
18
  ## File Service & Filtering (Lazy-Load Tree, Palette Index)
6
19
 
7
20
  **Component:** FileFilterService + API endpoints `/files/list`, `/files/index`, settings endpoints
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hienlh/ppm",
3
- "version": "0.22.0",
3
+ "version": "0.22.2",
4
4
  "description": "Personal Project Manager — mobile-first web IDE with AI assistance",
5
5
  "author": "hienlh",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -589,14 +589,39 @@ describe("webview-html: a panel narrower than the table", () => {
589
589
  expect(html).toContain('<div id="graph-clip"><div id="graph-svg-container"></div></div>');
590
590
  });
591
591
 
592
- it("puts the graph's scrollbar in the header, not on the graph", () => {
593
- // The overlay is as tall as the whole history, so a scrollbar along its
594
- // bottom edge would sit thousands of pixels below the viewport.
595
- const header = html.slice(html.indexOf('id="graph-header"'), html.indexOf('id="commit-list-wrapper"'));
596
- expect(header).toContain('id="graph-hscroll"');
592
+ it("pans the graph by dragging it, with no scrollbar taking a row", () => {
593
+ // A real scrollbar has nowhere to go: not on the overlay, which is as tall
594
+ // as the whole history, and not in the 24px header row, where the thumb
595
+ // comes down across the word "Graph". So the graph is dragged directly, and
596
+ // the only furniture is a hint that takes no space in the table.
597
+ expect(html).not.toContain("graph-hscroll");
598
+ expect(html).toMatch(/#graph-pan-bar \{[^}]*position: absolute/);
599
+ expect(html).toMatch(/#graph-pan-bar \{[^}]*pointer-events: none/);
597
600
  expect(html).toContain("transform: translateX(calc(-1 * var(--graph-pan-x, 0px)))");
598
601
  });
599
602
 
603
+ it("leaves the vertical scroll to the browser and the sideways drag to itself", () => {
604
+ // Without pan-y the finger that scrolls the list would be taken for a pan
605
+ // attempt on every row whose graph cell it happened to land on.
606
+ expect(html).toContain(".commit-row:not(.header-row) .col-graph { align-self: stretch; touch-action: pan-y; }");
607
+ expect(html).toContain("Math.abs(dx) <= Math.abs(e.clientY - startY)");
608
+ });
609
+
610
+ it("gives a row's graph cell a height to be dragged by, and spares the header's", () => {
611
+ // The cell is an empty spacer in a row that centres its cells, so it is 0px
612
+ // tall and every pointer lands on the row behind it. The header's cell has
613
+ // a label, and stretching that one lifts it off the other headings' line.
614
+ expect(html).toMatch(/\.commit-row:not\(\.header-row\) \.col-graph \{[^}]*align-self: stretch/);
615
+ expect(html).not.toMatch(/^\.commit-row \.col-graph \{/m);
616
+ });
617
+
618
+ it("does not select the commit a drag happened to end on", () => {
619
+ // The row's own click handler opens a commit, and a pan ends over a row.
620
+ const click = html.slice(html.indexOf("list.addEventListener('click'"));
621
+ expect(click.slice(0, 200)).toContain("e.stopPropagation()");
622
+ expect(click.slice(0, 200)).toContain("}, true)");
623
+ });
624
+
600
625
  it("agrees between the message column's floor and the cap that respects it", () => {
601
626
  // Two numbers for one thing: a CSS floor wider than the JS cap allows for
602
627
  // is a row that overflows its own box instead of one that fits.
@@ -99,7 +99,7 @@ ${getStyles()}
99
99
  <div id="graph-container">
100
100
  <div id="graph-header" class="commit-row header-row">
101
101
  <div class="col-refs">Branch / Tag</div>
102
- <div class="col-graph">Graph<div class="graph-hscroll hidden" id="graph-hscroll"><div id="graph-hscroll-inner"></div></div><div class="graph-resize-handle" id="graph-resize-handle"></div></div>
102
+ <div class="col-graph">Graph<div class="graph-resize-handle" id="graph-resize-handle"></div></div>
103
103
  <div class="col-message">Message</div>
104
104
  <div class="col-changes">Changes</div>
105
105
  <div class="col-author">Author</div>
@@ -112,6 +112,7 @@ ${getStyles()}
112
112
  </div>
113
113
  <div id="loading" class="loading hidden">Loading...</div>
114
114
  </div>
115
+ <div id="graph-pan-bar" class="hidden" aria-hidden="true"><div id="graph-pan-thumb"></div></div>
115
116
  <div id="scroll-markers" aria-hidden="true"></div>
116
117
  </div>
117
118
  <div id="detail-panel" class="detail-panel hidden"></div>
@@ -374,16 +375,24 @@ button:active { background: var(--surface); }
374
375
  .col-graph { width: var(--graph-col-w, 120px); min-width: var(--graph-col-w, 80px); overflow: hidden; flex-shrink: 0; position: relative; }
375
376
  .graph-resize-handle { position: absolute; right: 0; top: 0; bottom: 0; width: 6px; cursor: col-resize; z-index: 3; background: transparent; }
376
377
  .graph-resize-handle:hover, .graph-resize-handle.dragging { background: var(--blue); opacity: 0.5; }
377
- /* The graph's own horizontal scrollbar, in the sticky header. It cannot sit on
378
- the graph itself: that is one overlay as tall as the whole history, so a
379
- scrollbar along its bottom edge would be thousands of pixels below the
380
- viewport. This strip scrolls nothing — its scrollLeft is read and applied to
381
- the overlay as a translation. */
382
- .graph-hscroll { position: absolute; left: 0; right: 6px; bottom: 0; height: 7px; overflow-x: auto; overflow-y: hidden; }
383
- .graph-hscroll-inner { height: 1px; }
384
- .graph-hscroll::-webkit-scrollbar { height: 6px; }
385
- .graph-hscroll::-webkit-scrollbar-thumb { background: var(--border2); border-radius: 3px; }
386
- .graph-hscroll::-webkit-scrollbar-thumb:hover { background: var(--subtext); }
378
+ /* Panning the graph is a drag on the graph itself, so there is no scrollbar in
379
+ the table taking a row of its own — a real one cannot go on the overlay,
380
+ which is as tall as the whole history, and in a 24px header row the thumb
381
+ lands across the word "Graph". What is left is a hint: where in the lanes the
382
+ column currently is, shown while a drag or a wheel is moving it and faded out
383
+ after. It is positioned over the graph area rather than inside the scroller,
384
+ so it neither scrolls away nor displaces a row. */
385
+ /* A row's graph cell is an empty spacer, and the row centres its cells — so
386
+ without this it is 0px tall and the pointer that looks like it is on the
387
+ graph is really on the row behind it, which is what decides whether a drag
388
+ pans or selects a commit. Not the header's cell: that one has a label in it,
389
+ and stretching it takes the label off the line the other headings sit on. */
390
+ .commit-row:not(.header-row) .col-graph { align-self: stretch; touch-action: pan-y; }
391
+ :root.graph-can-pan .commit-row:not(.header-row) .col-graph { cursor: grab; }
392
+ :root.graph-panning .commit-row:not(.header-row) .col-graph { cursor: grabbing; }
393
+ #graph-pan-bar { position: absolute; z-index: 4; bottom: 6px; left: calc(var(--refs-col-w, 170px) + 6px); width: var(--graph-col-w, 120px); height: 4px; border-radius: 2px; background: color-mix(in srgb, var(--text) 10%, transparent); opacity: 0; transition: opacity 0.4s ease; pointer-events: none; }
394
+ #graph-pan-bar.visible { opacity: 1; transition-duration: 0.1s; }
395
+ #graph-pan-thumb { position: absolute; top: 0; bottom: 0; border-radius: 2px; background: var(--subtext); }
387
396
  /* The message never shrinks past a readable measure. Every other column is
388
397
  either fixed or hidden by the width tiers, so without a floor here this one
389
398
  absorbs the whole shortfall and disappears. */
@@ -454,7 +463,7 @@ button:active { background: var(--surface); }
454
463
  changes and author columns — it is positioned and they are not, so it wins
455
464
  the paint order — and capping the column without clipping only trades a
456
465
  missing message for one with branch lines drawn through it. What the clip
457
- hides is reached by panning: --graph-pan-x, driven by the header's strip. */
466
+ hides is reached by panning: --graph-pan-x, driven by a drag on the graph. */
458
467
  #commit-list-wrapper { position: relative; }
459
468
  #graph-clip { position: absolute; top: 0; left: calc(var(--refs-col-w, 170px) + 6px); width: var(--graph-col-w, 120px); height: 100%; overflow: hidden; z-index: 1; pointer-events: none; }
460
469
  /* The clip box starts where the graph cell starts — 6px being the row's own
@@ -702,10 +711,6 @@ button:active { background: var(--surface); }
702
711
  #graph-container { overflow-x: auto; overflow-y: auto; }
703
712
  #find-bar { order: 0; }
704
713
  #status-bar { order: 9; }
705
- /* A 6px scrollbar is a mouse's target. This is the only way to pan the graph
706
- without a wheel, so it gets a thumb a finger can land on. */
707
- .graph-hscroll { height: 14px; }
708
- .graph-hscroll::-webkit-scrollbar { height: 12px; }
709
714
  .commit-row.header-row { min-height: 20px; }
710
715
  .detail-panel { order: 8; max-height: 35vh; }
711
716
  }
@@ -1643,12 +1648,56 @@ document.getElementById('stash-save').addEventListener('click', () => {
1643
1648
 
1644
1649
  // --- Panning the graph inside its column ---
1645
1650
  {
1646
- const strip = document.getElementById('graph-hscroll');
1647
- strip.addEventListener('scroll', () => {
1648
- if (Math.abs(strip.scrollLeft - state.graphPanX) <= 1) return;
1649
- state.graphPanX = strip.scrollLeft;
1651
+ const list = document.getElementById('commit-list');
1652
+ let tracking = false, panned = false, startX = 0, startY = 0, startPan = 0, pointerId = null;
1653
+
1654
+ list.addEventListener('pointerdown', (e) => {
1655
+ if (e.button !== 0 || !e.target.closest || !e.target.closest('.col-graph')) return;
1656
+ if (graphPanMax() <= 0) return;
1657
+ tracking = true;
1658
+ panned = false;
1659
+ startX = e.clientX;
1660
+ startY = e.clientY;
1661
+ startPan = state.graphPanX;
1662
+ pointerId = e.pointerId;
1663
+ });
1664
+
1665
+ list.addEventListener('pointermove', (e) => {
1666
+ if (!tracking) return;
1667
+ const dx = e.clientX - startX;
1668
+ if (!panned) {
1669
+ // Sideways and past the slop before this is a pan: anything else is the
1670
+ // press that selects a commit, or the finger that scrolls the list —
1671
+ // .col-graph is touch-action: pan-y so the browser keeps that one.
1672
+ if (Math.abs(dx) < 4 || Math.abs(dx) <= Math.abs(e.clientY - startY)) return;
1673
+ panned = true;
1674
+ list.setPointerCapture(pointerId);
1675
+ document.documentElement.classList.add('graph-panning');
1676
+ showPanHint(true);
1677
+ }
1678
+ state.graphPanX = startPan - dx;
1650
1679
  applyGraphPan();
1651
1680
  });
1681
+
1682
+ const endPan = () => {
1683
+ if (!tracking) return;
1684
+ tracking = false;
1685
+ if (!panned) return;
1686
+ try { list.releasePointerCapture(pointerId); } catch (err) { /* already released */ }
1687
+ document.documentElement.classList.remove('graph-panning');
1688
+ showPanHint(false);
1689
+ };
1690
+ list.addEventListener('pointerup', endPan);
1691
+ list.addEventListener('pointercancel', endPan);
1692
+ // A drag is not a click. Captured on the way down, because the row's own
1693
+ // handler would otherwise open the commit the pan happened to end on.
1694
+ list.addEventListener('click', (e) => {
1695
+ if (!panned) return;
1696
+ panned = false;
1697
+ e.stopPropagation();
1698
+ e.preventDefault();
1699
+ }, true);
1700
+
1652
1701
  // A trackpad's sideways gesture and shift+wheel, over the list itself. The
1653
1702
  // overlay cannot take these: it is pointer-events: none so that a click lands
1654
1703
  // on the row underneath it.
@@ -2071,30 +2120,49 @@ function applyGraphColWidth() {
2071
2120
  applyGraphPan();
2072
2121
  }
2073
2122
 
2123
+ /** How far the graph can be moved: what is drawn, less what the column shows. */
2124
+ function graphPanMax() {
2125
+ const cell = document.querySelector('#graph-header .col-graph');
2126
+ return Math.max(0, state.graphWidth - (cell ? cell.offsetWidth : 0));
2127
+ }
2128
+
2074
2129
  /**
2075
- * Move the overlay inside its clip box, and keep the header's scroll strip
2076
- * saying the same thing. The strip scrolls nothing itself — it exists because
2077
- * a scrollbar on the overlay would sit below the last commit in the history.
2130
+ * Move the overlay inside its clip box, and put the hint where the column now
2131
+ * is. The hint is not a scrollbar: it takes no room in the table and is only
2132
+ * legible while something is moving the graph.
2078
2133
  */
2079
2134
  function applyGraphPan() {
2080
2135
  const cell = document.querySelector('#graph-header .col-graph');
2081
2136
  const shown = cell ? cell.offsetWidth : 0;
2082
- const max = Math.max(0, state.graphWidth - shown);
2137
+ const max = graphPanMax();
2083
2138
  state.graphPanX = Math.min(Math.max(0, state.graphPanX), max);
2084
2139
  document.documentElement.style.setProperty('--graph-pan-x', state.graphPanX + 'px');
2140
+ document.documentElement.classList.toggle('graph-can-pan', max > 0);
2141
+
2142
+ const bar = document.getElementById('graph-pan-bar');
2143
+ const thumb = document.getElementById('graph-pan-thumb');
2144
+ if (!bar || !thumb) return;
2145
+ bar.classList.toggle('hidden', max <= 0 || !state.graphWidth);
2146
+ if (max <= 0 || !state.graphWidth) return;
2147
+ const ratio = shown / state.graphWidth;
2148
+ thumb.style.width = (ratio * 100) + '%';
2149
+ thumb.style.left = ((state.graphPanX / state.graphWidth) * 100) + '%';
2150
+ }
2085
2151
 
2086
- const strip = document.getElementById('graph-hscroll');
2087
- const inner = document.getElementById('graph-hscroll-inner');
2088
- if (!strip || !inner) return;
2089
- inner.style.width = state.graphWidth + 'px';
2090
- strip.classList.toggle('hidden', max <= 0);
2091
- // Guarded, or writing scrollLeft here re-enters through the scroll handler.
2092
- if (Math.abs(strip.scrollLeft - state.graphPanX) > 1) strip.scrollLeft = state.graphPanX;
2152
+ /** Show the hint while something is moving the graph, then let it fade. */
2153
+ let panHintTimer = null;
2154
+ function showPanHint(sticky) {
2155
+ const bar = document.getElementById('graph-pan-bar');
2156
+ if (!bar || bar.classList.contains('hidden')) return;
2157
+ bar.classList.add('visible');
2158
+ if (panHintTimer) clearTimeout(panHintTimer);
2159
+ panHintTimer = sticky ? null : setTimeout(() => bar.classList.remove('visible'), 700);
2093
2160
  }
2094
2161
 
2095
2162
  function panGraphBy(dx) {
2096
2163
  state.graphPanX += dx;
2097
2164
  applyGraphPan();
2165
+ showPanHint(false);
2098
2166
  }
2099
2167
 
2100
2168
  function graphVertexOver(e) {
@@ -155,6 +155,7 @@ export async function initProject(options: InitOptions = {}) {
155
155
  configService.set("auth", { enabled: authEnabled, token: authToken });
156
156
  configService.set("ai", {
157
157
  default_provider: "claude",
158
+ new_chat_provider_mode: configService.get("ai").new_chat_provider_mode,
158
159
  providers: {
159
160
  claude: {
160
161
  type: "agent-sdk",
@@ -1,8 +1,9 @@
1
1
  import type { ChatMessage, ChatEvent, SessionInfo } from "../provider.interface.ts";
2
2
  import { stripSharedContext } from "../../shared/provider-context.ts";
3
- import { readdirSync, readFileSync, statSync } from "node:fs";
3
+ import { readdirSync, readFileSync, statSync, realpathSync } from "node:fs";
4
4
  import { join, resolve } from "node:path";
5
5
  import { homedir } from "node:os";
6
+ import { getPpmDir } from "../../services/ppm-dir.ts";
6
7
  import { redactTruncate } from "./codex-redact.ts";
7
8
  import { parseApplyPatch, changeToToolUse } from "./codex-patch.ts";
8
9
  import { mapRolloutItem } from "./codex-rollout-items.ts";
@@ -449,14 +450,27 @@ export function getRolloutMessages(sessionsDir: string, threadId: string, reques
449
450
 
450
451
  /** True when a path points at a codex rollout in the ambient or a PPM account home. */
451
452
  export function isCodexRolloutPath(p: string): boolean {
452
- const n = normPath(p);
453
- const ambient = normPath(join(homedir(), ".codex", "sessions"));
454
- const managed = normPath(join(homedir(), ".ppm", "codex-accounts"));
455
- return n.endsWith(".jsonl") && (n.includes(ambient) || (n.includes(managed) && n.includes("/sessions/")));
453
+ return withinCodexSessions(p, false);
454
+ }
455
+
456
+ function withinCodexSessions(p: string, canonical: boolean): boolean {
457
+ const normalize = (value: string) => process.platform === "win32" ? normPath(value).replace(/\\/g, "/") : normPath(value);
458
+ const n = normalize(p);
459
+ if (!n.endsWith(".jsonl")) return false;
460
+ const rootPath = (value: string) => {
461
+ if (canonical) { try { return normalize(realpathSync(value)); } catch { return null; } }
462
+ return normalize(value);
463
+ };
464
+ const ambientRoots = [join(homedir(), ".codex", "sessions")];
465
+ if (process.env.CODEX_HOME) ambientRoots.push(join(process.env.CODEX_HOME, "sessions"));
466
+ if (ambientRoots.some((value) => { const root = rootPath(value); return root && n.startsWith(root + "/"); })) return true;
467
+ const managed = rootPath(join(getPpmDir(), "codex-accounts"));
468
+ return !!managed && n.startsWith(managed + "/") && /^[^/]+\/sessions\/.+/.test(n.slice(managed.length + 1));
456
469
  }
457
470
 
458
471
  /**
459
- * One pre-compact segment for the "load more" feature. Jails to ~/.codex/sessions,
472
+ * One pre-compact segment for the "load more" feature. Jails to ambient,
473
+ * CODEX_HOME, or PPM managed-account session directories via real paths;
460
474
  * fail-closed on cwd.
461
475
  *
462
476
  * One segment per request rather than everything before the first boundary, which is
@@ -468,7 +482,12 @@ export function isCodexRolloutPath(p: string): boolean {
468
482
  */
469
483
  export function getCodexPreCompactMessages(file: string, requestedCwd?: string, beforeId?: string): ChatMessage[] {
470
484
  if (!isCodexRolloutPath(file)) throw new Error("Access denied: not a codex rollout");
471
- const resolved = resolve(file);
485
+ let resolved: string;
486
+ try { resolved = realpathSync(resolve(file)); } catch { throw new Error("File not found"); }
487
+ if (!withinCodexSessions(resolved, true)) throw new Error("Access denied: transcript outside Codex sessions");
488
+ const info = statSync(resolved);
489
+ if (!info.isFile()) throw new Error("Not a regular file");
490
+ if (info.size > 256 * 1024 * 1024) throw new Error("Transcript too large");
472
491
  let text: string;
473
492
  try { text = readFileSync(resolved, "utf-8"); } catch { throw new Error("File not found"); }
474
493
  // Read once and reused below. `readSessionMeta` opens and reads the whole rollout again, and
@@ -1147,12 +1147,14 @@ export class CodexAppServerProvider implements AIProvider {
1147
1147
  }
1148
1148
 
1149
1149
  /** Read the session's account without advancing the account-selection strategy. */
1150
- async getUsage(sessionId?: string): Promise<UsageInfo> {
1150
+ async getUsage(sessionId?: string, pickedAccountId?: string): Promise<UsageInfo> {
1151
1151
  // Idempotent, and needed because a caller can reach the provider before the
1152
1152
  // server has started background polling — an unregistered source would make
1153
1153
  // the shared layer answer {} instead of reading codex.
1154
1154
  registerUsageSource(codexUsageSource);
1155
- const accountId = sessionId ? getSessionCodexAccount(sessionId) : null;
1155
+ // An unopened chat has a claimed account but no session binding yet.
1156
+ // Read that exact account without advancing the selection strategy.
1157
+ const accountId = sessionId ? getSessionCodexAccount(sessionId) : pickedAccountId ?? null;
1156
1158
  const bound = accountId ? getCodexAccount(accountId) : null;
1157
1159
  if (bound) {
1158
1160
  // Through the shared layer, so this answers from the cache or the stored
@@ -1166,12 +1168,12 @@ export class CodexAppServerProvider implements AIProvider {
1166
1168
  };
1167
1169
  }
1168
1170
 
1169
- // Nothing bound yet — a session binds on its first send. Name the account
1171
+ // No binding or explicit tab claim — a session binds on its first send. Name the account
1170
1172
  // that will serve it, but do NOT read its quota: that spawns an app-server
1171
1173
  // against a login which is not yet this session's, and which a round-robin
1172
1174
  // or lowest-usage pick may never hand it. So the toolbar can say WHO will
1173
1175
  // answer without PPM touching that account on a session's behalf before
1174
- // the session owns it. The numbers arrive with the first turn.
1176
+ // the session owns it. Quota is read once a tab claims it or a session binds it.
1175
1177
  //
1176
1178
  // Skipped when the session names an account that no longer exists: naming
1177
1179
  // a different one there would be a lie about a binding that already failed.
@@ -135,7 +135,7 @@ chatRoutes.get("/usage", async (c) => {
135
135
  const { invalidateUsage } = await import("../../services/provider-usage/usage-registry.ts");
136
136
  invalidateUsage(providerId);
137
137
  }
138
- try { return c.json(ok(await provider.getUsage(c.req.query("session")))); } catch { return c.json(ok({})); }
138
+ try { return c.json(ok(await provider.getUsage(c.req.query("session"), c.req.query("accountId")))); } catch { return c.json(ok({})); }
139
139
  }
140
140
  return c.json(ok({}));
141
141
  }