@hienlh/ppm 0.21.0 → 0.22.0

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 (606) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/CLAUDE.md +29 -4
  3. package/THIRD-PARTY-NOTICES.md +1 -1
  4. package/assets/skills/ppm/SKILL.md +1 -1
  5. package/assets/skills/ppm/references/http-api.md +1 -1
  6. package/bun.lock +16 -1
  7. package/dist/web/assets/accounts-settings-section-BVr9-FZJ.js +1 -0
  8. package/dist/web/assets/{ai-resource-editor-F09pf8Hd.js → ai-resource-editor-CdDpOwjh.js} +1 -1
  9. package/dist/web/assets/ai-settings-section-B1hQyjAd.js +1 -0
  10. package/dist/web/assets/ai-settings-section-X2UOfSaS.js +1 -0
  11. package/dist/web/assets/{angular-html-DkR4iCth.js → angular-html-G66FNlyn.js} +1 -1
  12. package/dist/web/assets/{angular-ts-DcyL4Chx.js → angular-ts-B-PnUMyt.js} +1 -1
  13. package/dist/web/assets/{api-client-bfB7cXRM.js → api-client-C1ZwRL4Y.js} +1 -1
  14. package/dist/web/assets/api-client-pQL53cDQ.js +1 -0
  15. package/dist/web/assets/{api-settings-BcNjZIvG.js → api-settings-_7Q-ULxp.js} +1 -1
  16. package/dist/web/assets/{api-themes-DBfyVUtX.js → api-themes-DRAERo4Q.js} +1 -1
  17. package/dist/web/assets/{apl-BVlrVtjt.js → apl-BmDqqwE8.js} +1 -1
  18. package/dist/web/assets/{appearance-settings-section-whgnq3ss.js → appearance-settings-section-DLoPK0LD.js} +1 -1
  19. package/dist/web/assets/architecture-PBZL5I3N-Dx6nBEKT.js +1 -0
  20. package/dist/web/assets/{astro-MjJlozLj.js → astro-BfwMNfxr.js} +1 -1
  21. package/dist/web/assets/{audio-preview-Bc54GCAV.js → audio-preview-DsYvgKqj.js} +1 -1
  22. package/dist/web/assets/{blade-DnJWi1fz.js → blade-BsFV4pnN.js} +1 -1
  23. package/dist/web/assets/branch-review-tab-G84Ojzae.js +1 -0
  24. package/dist/web/assets/{bsl-DCmJxSIz.js → bsl-Cw9Outng.js} +1 -1
  25. package/dist/web/assets/{button-VW4QGKCn.js → button-B9CTrsEf.js} +1 -1
  26. package/dist/web/assets/{cairo-C8dDenub.js → cairo-DOK5Wqnd.js} +1 -1
  27. package/dist/web/assets/chat-tab-DQJfjm8E.js +15 -0
  28. package/dist/web/assets/{cobol-DPx_nifl.js → cobol-DRaRy4FK.js} +1 -1
  29. package/dist/web/assets/code-editor-D04iUPsZ.js +7 -0
  30. package/dist/web/assets/{codex-daily-guard-CfybHv_c.js → codex-daily-guard-BSccLRGT.js} +1 -1
  31. package/dist/web/assets/{coffee-DvFzqhgN.js → coffee-DvXuBmEJ.js} +1 -1
  32. package/dist/web/assets/{conflict-editor-DenQUOT2.js → conflict-editor-C4ay676e.js} +2 -2
  33. package/dist/web/assets/context-menu-C6pIsf83.js +1 -0
  34. package/dist/web/assets/{cpp-BLJN0umi.js → cpp-zCmm8rKA.js} +1 -1
  35. package/dist/web/assets/{crystal-BXR-9fGw.js → crystal-CdQx6lB6.js} +1 -1
  36. package/dist/web/assets/{csv-preview-CClrJOuR.js → csv-preview-DbwEmG7o.js} +1 -1
  37. package/dist/web/assets/{data-grid-overlay-editor-D4fhcMfT.js → data-grid-overlay-editor-6xUCeLHh.js} +1 -1
  38. package/dist/web/assets/{database-viewer-DItjm8wB.js → database-viewer-S6LsALjX.js} +1 -1
  39. package/dist/web/assets/{dialog-DQTn55Mt.js → dialog-B84_DJQ8.js} +1 -1
  40. package/dist/web/assets/diff-viewer-3oBA7vgQ.js +4 -0
  41. package/dist/web/assets/{docx-preview-DYBkiUaE.js → docx-preview-CXuk-emm.js} +1 -1
  42. package/dist/web/assets/{edge-rWOlCmXT.js → edge-E8QLt7oi.js} +1 -1
  43. package/dist/web/assets/{edit-diff-preview-COtmo54G.js → edit-diff-preview-BGGGecVw.js} +1 -1
  44. package/dist/web/assets/{editor-language-service-DxkZ51E5.js → editor-language-service-DJmaKVY0.js} +1 -1
  45. package/dist/web/assets/{elixir-E7Y82okW.js → elixir-DPb_-72P.js} +1 -1
  46. package/dist/web/assets/{elm-DTPGbc-w.js → elm-BMzJY5b0.js} +1 -1
  47. package/dist/web/assets/{erb-DHqLHrE6.js → erb-AABQNtMK.js} +1 -1
  48. package/dist/web/assets/{erlang-Byg0nNiV.js → erlang-CEIKOkfP.js} +1 -1
  49. package/dist/web/assets/{esm-D_DmOM7n.js → esm-B9RVMpwV.js} +1 -1
  50. package/dist/web/assets/explorer-body-BQ0jyAGv.js +2 -0
  51. package/dist/web/assets/explorer-window-content-jdkWt34J.js +1 -0
  52. package/dist/web/assets/{extension-manager-section-BhwB6nqr.js → extension-manager-section-DG1guILi.js} +1 -1
  53. package/dist/web/assets/{extension-webview-DI4VL2tJ.js → extension-webview-BGSDUbdK.js} +1 -1
  54. package/dist/web/assets/file-icons-BXdvggK2.js +1 -0
  55. package/dist/web/assets/file-icons.generated-BtBErEwb.css +1 -0
  56. package/dist/web/assets/file-store-ojgMXgAw.js +1 -0
  57. package/dist/web/assets/files-settings-section-Gnyd7han.js +1 -0
  58. package/dist/web/assets/{format-file-meta-DOpzJJhS.js → format-file-meta-C77h04cZ.js} +1 -1
  59. package/dist/web/assets/{fortran-fixed-form-CY1n0Xf6.js → fortran-fixed-form-CrXvLHyi.js} +1 -1
  60. package/dist/web/assets/{fsharp-_vEDSCJz.js → fsharp-DCI4qAz_.js} +1 -1
  61. package/dist/web/assets/{gdresource-B6XT0wAC.js → gdresource-Vb2kO1MG.js} +1 -1
  62. package/dist/web/assets/{general-settings-section-BcxwiZ0X.js → general-settings-section-V6Khh15V.js} +1 -1
  63. package/dist/web/assets/{git-commit-Wk9ovq59.js → git-commit-BMI_OWn-.js} +1 -1
  64. package/dist/web/assets/git-log-panel-Bi513qjT.js +1 -0
  65. package/dist/web/assets/{git-rebase-DSarGPvK.js → git-rebase-D-d_2iqZ.js} +1 -1
  66. package/dist/web/assets/{git-repo-store-Dy69YCHN.js → git-repo-store-88Z8v-pF.js} +1 -1
  67. package/dist/web/assets/gitGraph-HDMCJU4V-C6qRruHI.js +1 -0
  68. package/dist/web/assets/{glide-data-grid-BvgAqSUr.js → glide-data-grid-DnQwYSmQ.js} +7 -7
  69. package/dist/web/assets/{glimmer-js-CkYI35_P.js → glimmer-js-BmNNiR0Z.js} +1 -1
  70. package/dist/web/assets/{glimmer-ts-BqmIZcds.js → glimmer-ts-DQE2Ufha.js} +1 -1
  71. package/dist/web/assets/{glsl-Be-yBCF5.js → glsl-BcIDsxRJ.js} +1 -1
  72. package/dist/web/assets/{graphql-a5DrENHY.js → graphql-DGKSLmGH.js} +1 -1
  73. package/dist/web/assets/group-chat-tab-DpczdwyV.js +1 -0
  74. package/dist/web/assets/{hack-BLAg20aD.js → hack-YiC8bEIb.js} +1 -1
  75. package/dist/web/assets/{haml-Dj6bGOQH.js → haml-DjblxAsz.js} +1 -1
  76. package/dist/web/assets/{handlebars-dmEa36Oh.js → handlebars-BQepOXaU.js} +1 -1
  77. package/dist/web/assets/{html-yacZYzSE.js → html-Cayj_2GP.js} +1 -1
  78. package/dist/web/assets/{html-derivative-lLi3-RNT.js → html-derivative-Weq79bL6.js} +1 -1
  79. package/dist/web/assets/{http-1KaxcE4e.js → http-BeLfdGdz.js} +1 -1
  80. package/dist/web/assets/{hurl-ST-dEEZR.js → hurl-D9rypCLK.js} +1 -1
  81. package/dist/web/assets/{hxml-CpDtuq__.js → hxml-clG8mmSH.js} +1 -1
  82. package/dist/web/assets/{image-preview-CJVxL0uH.js → image-preview-CzCc4_Do.js} +1 -1
  83. package/dist/web/assets/image-preview-geometry-9lgeLYB3.js +1 -0
  84. package/dist/web/assets/index-BH99TIE0.css +1 -0
  85. package/dist/web/assets/index-DU68xk06.js +52 -0
  86. package/dist/web/assets/info-3K5VOQVL-BOSpdwlz.js +1 -0
  87. package/dist/web/assets/{jinja-Cjw-evAn.js → jinja-BShBWfs5.js} +1 -1
  88. package/dist/web/assets/{jira-watcher-section-DIC1yIV3.js → jira-watcher-section-f2HUbGDY.js} +1 -1
  89. package/dist/web/assets/{jison-DfATMYoY.js → jison-dN2Nf_Dr.js} +1 -1
  90. package/dist/web/assets/{julia-DDCaxiqT.js → julia-5U0vR-uJ.js} +1 -1
  91. package/dist/web/assets/{just-CJL529u6.js → just-BMppVRy6.js} +1 -1
  92. package/dist/web/assets/keybindings-store-DCRsnKg5.js +1 -0
  93. package/dist/web/assets/{keyboard-shortcuts-section-BW03P3AF.js → keyboard-shortcuts-section-BpvVEw_Y.js} +1 -1
  94. package/dist/web/assets/{latex-Zc4EpLpW.js → latex-DSSOKjis.js} +1 -1
  95. package/dist/web/assets/{liquid-B_6KMNYS.js → liquid-C24dKoyI.js} +1 -1
  96. package/dist/web/assets/{lua-BtS2idK7.js → lua-DNOsnyhn.js} +1 -1
  97. package/dist/web/assets/markdown-renderer-Bcaf9_bA.js +1 -0
  98. package/dist/web/assets/markdown-renderer-DGJp8HC3.js +3 -0
  99. package/dist/web/assets/{marko-Wk6LBnck.js → marko-26MHO_Q9.js} +1 -1
  100. package/dist/web/assets/{mdc-cRaXBrzL.js → mdc-Dhw9mGrC.js} +1 -1
  101. package/dist/web/assets/{media-url-DL8OCGGg.js → media-url-yfqHpPz0.js} +1 -1
  102. package/dist/web/assets/{mobile-bottom-sheet-CoUauvLF.js → mobile-bottom-sheet-C3TniEP-.js} +1 -1
  103. package/dist/web/assets/mobile-explorer-sheet-Bol3XAO1.js +1 -0
  104. package/dist/web/assets/{nextflow-Bos2wvDV.js → nextflow-xH4oJTex.js} +1 -1
  105. package/dist/web/assets/{nginx-DMlOwzdr.js → nginx-CMz4w0M8.js} +1 -1
  106. package/dist/web/assets/{nim-D2dZQA88.js → nim-D8FnIYsx.js} +1 -1
  107. package/dist/web/assets/notification-store-BXt54VIn.js +1 -0
  108. package/dist/web/assets/{notifications-settings-section-DPUROMw-.js → notifications-settings-section-DqlPDfNX.js} +1 -1
  109. package/dist/web/assets/{number-overlay-editor-DZ2i_Bnb.js → number-overlay-editor-CRGwULhR.js} +1 -1
  110. package/dist/web/assets/{overview-panel-R146OIom.js → overview-panel-Di-3aV9p.js} +1 -1
  111. package/dist/web/assets/packet-RMMSAZCW-BGIE1QdU.js +1 -0
  112. package/dist/web/assets/panel-store-CboGJAwh.js +1 -0
  113. package/dist/web/assets/{pdf-preview-BWUPGLp6.js → pdf-preview-CmBI6E7d.js} +1 -1
  114. package/dist/web/assets/{perl-C5-Z6fPn.js → perl-D9iNWYwb.js} +1 -1
  115. package/dist/web/assets/{php-BXNNhkMH.js → php-Bw3Qo0Zv.js} +1 -1
  116. package/dist/web/assets/pie-UPGHQEXC-Dg-QTbXX.js +1 -0
  117. package/dist/web/assets/{postgres-viewer-CjOvaruz.js → postgres-viewer-8ZAqEALv.js} +3 -3
  118. package/dist/web/assets/{ppmbot-settings-section-DrvWh8l8.js → ppmbot-settings-section-DKJUA3Kq.js} +1 -1
  119. package/dist/web/assets/problems-panel-CFcoUx8h.js +1 -0
  120. package/dist/web/assets/{process-table-BRMS1MrX.js → process-table-EKr2dqG6.js} +1 -1
  121. package/dist/web/assets/project-framework-store-B70Q3dNM.js +1 -0
  122. package/dist/web/assets/project-store-B87hLRSk.js +1 -0
  123. package/dist/web/assets/{proxy-settings-section-Bv0Ny-Yy.js → proxy-settings-section-B7qRVl8w.js} +1 -1
  124. package/dist/web/assets/{pug-C1zf1yWy.js → pug-fT5JC6Vm.js} +1 -1
  125. package/dist/web/assets/{qml-OW58jGpD.js → qml-P_wEDB1w.js} +1 -1
  126. package/dist/web/assets/{query-audit-section-BcHJZcVC.js → query-audit-section-By6qyqdB.js} +1 -1
  127. package/dist/web/assets/radar-KQ55EAFF-DZ6IVu2s.js +1 -0
  128. package/dist/web/assets/{razor-DzjSaobM.js → razor-Drrt2_Ph.js} +1 -1
  129. package/dist/web/assets/remote-desktop-clipboard-notice-0WDYsyCA.js +1 -0
  130. package/dist/web/assets/remote-desktop-mobile-sheet-COYDg16H.js +2 -0
  131. package/dist/web/assets/remote-desktop-mobile-view-2DqSuS2U.js +1 -0
  132. package/dist/web/assets/{remote-desktop-readiness-gate-BTzss2wX.js → remote-desktop-readiness-gate-Dm_qMJMH.js} +1 -1
  133. package/dist/web/assets/remote-desktop-window-content-UodhsF7W.js +1 -0
  134. package/dist/web/assets/{rst-RfuihOUW.js → rst-Dr2mW4-h.js} +1 -1
  135. package/dist/web/assets/{ruby-CNO3pli1.js → ruby-C6EpUsTY.js} +1 -1
  136. package/dist/web/assets/{run-in-terminal-Cb_hVU8x.js → run-in-terminal-CdefEe_R.js} +1 -1
  137. package/dist/web/assets/{sas-DZY5BtXx.js → sas-D8ZEeP6v.js} +1 -1
  138. package/dist/web/assets/{schedules-settings-section-DO8xAOVj.js → schedules-settings-section-bG2lYfGv.js} +1 -1
  139. package/dist/web/assets/{scroll-area-LtNV8g5o.js → scroll-area-BzeiOd-B.js} +1 -1
  140. package/dist/web/assets/{scss--4g-REcN.js → scss-ITL7jQ1f.js} +1 -1
  141. package/dist/web/assets/{send-to-chat-Cnpru1M0.js → send-to-chat-F207yH3u.js} +1 -1
  142. package/dist/web/assets/{separator-Cds1yxy8.js → separator-TRMsJUYe.js} +1 -1
  143. package/dist/web/assets/settings-body-C06bFarv.js +2 -0
  144. package/dist/web/assets/settings-store-krC6bc79.js +2 -0
  145. package/dist/web/assets/settings-tab-34RSaASd.js +1 -0
  146. package/dist/web/assets/settings-window-content-Dr_2lmHH.js +1 -0
  147. package/dist/web/assets/{shaderlab-Cu4sBiVn.js → shaderlab-mW-IIlhk.js} +1 -1
  148. package/dist/web/assets/{shellsession-DWzJfEv7.js → shellsession-DhNl5STt.js} +1 -1
  149. package/dist/web/assets/{soy-BnqE-5Ff.js → soy-SdY0Qjtr.js} +1 -1
  150. package/dist/web/assets/{sparql-Ctg__NhZ.js → sparql-ftqZo2i-.js} +1 -1
  151. package/dist/web/assets/{sql-query-editor-D9k19T_M.js → sql-query-editor-BgRH0cHD.js} +1 -1
  152. package/dist/web/assets/{sqlite-viewer-DoV9hnWJ.js → sqlite-viewer-DUxGslpa.js} +1 -1
  153. package/dist/web/assets/{stata-Qk2h66XW.js → stata-Bj7Yjb4b.js} +1 -1
  154. package/dist/web/assets/{surrealql-8QEX7EfH.js → surrealql-CLhYGAfB.js} +1 -1
  155. package/dist/web/assets/{svelte-D_mubC5-.js → svelte-DTAlRHhK.js} +1 -1
  156. package/dist/web/assets/system-monitor-body-IZuLn7Qh.js +2 -0
  157. package/dist/web/assets/system-monitor-tab-DiSA490Z.js +1 -0
  158. package/dist/web/assets/{system-monitor-window-content-qXLcicFM.js → system-monitor-window-content-CEaFISgj.js} +1 -1
  159. package/dist/web/assets/{tab-host-window-content-7fPZNEni.js → tab-host-window-content-D0TN8Wrq.js} +1 -1
  160. package/dist/web/assets/{tab-store-DGJGnbwv.js → tab-store-OJa1GPCq.js} +1 -1
  161. package/dist/web/assets/team-member-sheet-DDPNcCjd.js +2 -0
  162. package/dist/web/assets/{team-member-window-content-BgvJFypM.js → team-member-window-content-DCiW_1-E.js} +1 -1
  163. package/dist/web/assets/{templ-C1jV0fsO.js → templ-BMfhACxb.js} +1 -1
  164. package/dist/web/assets/{terminal-tab-Cs0sGljx.js → terminal-tab-4ypP0NDY.js} +3 -3
  165. package/dist/web/assets/{tex-D_StFXy_.js → tex-BcssM-QA.js} +1 -1
  166. package/dist/web/assets/tool-cards-Dd6E7Q3k.js +9 -0
  167. package/dist/web/assets/treemap-KZPCXAKY-CE30wCXa.js +1 -0
  168. package/dist/web/assets/{ts-tags-B5m47rEm.js → ts-tags-CrR5IYI8.js} +1 -1
  169. package/dist/web/assets/{twig-C_br6GIl.js → twig-DchfUmyI.js} +1 -1
  170. package/dist/web/assets/{use-blob-url-C-SGi_Hb.js → use-blob-url-CPtCcY3i.js} +1 -1
  171. package/dist/web/assets/{use-inline-blame-B4trxGcR.js → use-inline-blame-vrfFHuaR.js} +1 -1
  172. package/dist/web/assets/{use-lsp-CV0vzILO.js → use-lsp-CsuZu49X.js} +1 -1
  173. package/dist/web/assets/{use-monaco-theme-DBqbWjWX.js → use-monaco-theme-BIVYJBIn.js} +1 -1
  174. package/dist/web/assets/{use-open-team-member-DENsjp0s.js → use-open-team-member-C3mrUzIC.js} +1 -1
  175. package/dist/web/assets/{use-websocket-LgXiX1qN.js → use-websocket-BMbufhX7.js} +1 -1
  176. package/dist/web/assets/{vendor-mermaid-Cbig5zA6.js → vendor-mermaid-BitiXJlL.js} +1 -1
  177. package/dist/web/assets/{vendor-ui-BBdkypKW.js → vendor-ui-DmZ-Mb-H.js} +1 -1
  178. package/dist/web/assets/video-preview-Byj8P_jA.js +1 -0
  179. package/dist/web/assets/{vue-DQwCnLdr.js → vue-1EL6Pw1h.js} +1 -1
  180. package/dist/web/assets/{vue-html-T_EoGpPJ.js → vue-html-BPQNwPC6.js} +1 -1
  181. package/dist/web/assets/{vue-vine-DmxLqX50.js → vue-vine-Bb_dsWLt.js} +1 -1
  182. package/dist/web/assets/{window-pip-registry-3l4hoxuD.js → window-pip-registry-yD6esSju.js} +1 -1
  183. package/dist/web/assets/ws-auth-CEYVQ8NH.js +1 -0
  184. package/dist/web/assets/{xml-Bs6Bw8is.js → xml-dxkLNA0K.js} +1 -1
  185. package/dist/web/assets/{xsl-f5ZNhRCc.js → xsl-Bw2adwrl.js} +1 -1
  186. package/dist/web/index.html +27 -26
  187. package/dist/web/sw.js +1 -1
  188. package/docs/architecture/plugins-and-tracing.md +285 -0
  189. package/docs/lessons-learned.md +31 -0
  190. package/docs/system-architecture.md +1 -0
  191. package/package.json +2 -1
  192. package/packages/ext-git-graph/src/compare-diff-base.test.ts +173 -0
  193. package/packages/ext-git-graph/src/compare-view.ts +22 -3
  194. package/packages/ext-git-graph/src/extension.ts +6 -0
  195. package/packages/ext-git-graph/src/types.ts +19 -0
  196. package/packages/ext-git-graph/src/webview-html.test.ts +134 -1
  197. package/packages/ext-git-graph/src/webview-html.ts +277 -27
  198. package/scripts/gen-file-icons.ts +493 -242
  199. package/src/providers/claude-agent-sdk.ts +42 -1
  200. package/src/providers/codex-app-server/codex-history.ts +77 -23
  201. package/src/server/index.ts +42 -7
  202. package/src/server/routes/chat.ts +15 -11
  203. package/src/server/routes/git.ts +83 -3
  204. package/src/server/routes/remote-desktop.ts +7 -2
  205. package/src/server/ws/global.ts +1 -1
  206. package/src/server/ws/remote-desktop.ts +12 -5
  207. package/src/services/binary-content.ts +25 -0
  208. package/src/services/chat-search.service.ts +1 -1
  209. package/src/services/chat.service.ts +9 -0
  210. package/src/services/event-loop-lag.ts +208 -0
  211. package/src/services/file-lines.ts +114 -0
  212. package/src/services/file-watcher/recreated-dir-poller.ts +108 -6
  213. package/src/services/file-watcher/watch-tree.ts +131 -10
  214. package/src/services/file-watcher.service.ts +73 -23
  215. package/src/services/git-branch-diff/branch-diff-parse.ts +151 -0
  216. package/src/services/git-branch-diff/branch-diff.service.ts +79 -0
  217. package/src/services/git-hunks/git-hunks.service.ts +26 -11
  218. package/src/services/git-hunks/unified-diff.ts +15 -1
  219. package/src/services/git.service.ts +74 -24
  220. package/src/services/jsonl-transcript-parser.ts +138 -6
  221. package/src/services/log-rotate.ts +142 -0
  222. package/src/services/media-transcode/ffmpeg-capabilities.ts +72 -2
  223. package/src/services/remote-desktop/remote-desktop-audio.ts +193 -0
  224. package/src/services/remote-desktop/remote-desktop-capture-input.ts +87 -14
  225. package/src/services/remote-desktop/remote-desktop-capture.ts +40 -7
  226. package/src/services/remote-desktop/remote-desktop-clipboard.ts +166 -0
  227. package/src/services/remote-desktop/remote-desktop-displays.ts +77 -1
  228. package/src/services/remote-desktop/remote-desktop-encoder-args.ts +25 -6
  229. package/src/services/remote-desktop/remote-desktop-evdev-key-map.ts +69 -0
  230. package/src/services/remote-desktop/remote-desktop-input-linux.ts +253 -0
  231. package/src/services/remote-desktop/remote-desktop-input-uinput.ts +242 -0
  232. package/src/services/remote-desktop/remote-desktop-input.ts +40 -8
  233. package/src/services/remote-desktop/remote-desktop-linux-session.ts +146 -0
  234. package/src/services/remote-desktop/remote-desktop-ogg-opus.ts +144 -0
  235. package/src/services/remote-desktop/remote-desktop-privacy.ts +163 -0
  236. package/src/services/remote-desktop/remote-desktop-quality.ts +138 -0
  237. package/src/services/remote-desktop/remote-desktop-requirements.ts +158 -5
  238. package/src/services/remote-desktop/remote-desktop-resolution.ts +302 -0
  239. package/src/services/remote-desktop/remote-desktop-session.ts +523 -21
  240. package/src/services/remote-desktop/remote-desktop-x11.ts +250 -0
  241. package/src/services/supervisor.ts +47 -4
  242. package/src/shared/remote-desktop-custom-quality.ts +117 -0
  243. package/src/shared/remote-desktop-quality.ts +127 -0
  244. package/src/types/chat.ts +4 -0
  245. package/src/types/git.ts +52 -0
  246. package/src/web/components/branch-review/branch-review-tab.tsx +360 -0
  247. package/src/web/components/branch-review/branch-review-tree-row.tsx +138 -0
  248. package/src/web/components/chat/attachment-chips.tsx +30 -6
  249. package/src/web/components/chat/chat-tab.tsx +12 -1
  250. package/src/web/components/chat/message-events.tsx +273 -0
  251. package/src/web/components/chat/message-list.tsx +107 -861
  252. package/src/web/components/chat/message-markdown.tsx +34 -0
  253. package/src/web/components/chat/message-media.tsx +174 -0
  254. package/src/web/components/chat/message-user-bubble.tsx +399 -0
  255. package/src/web/components/chat/provider-selector.tsx +30 -22
  256. package/src/web/components/chat/session-history-list.tsx +2 -2
  257. package/src/web/components/chat/session-list-panel.tsx +3 -2
  258. package/src/web/components/editor/binary-diff-view.tsx +212 -0
  259. package/src/web/components/editor/diff-viewer.tsx +76 -3
  260. package/src/web/components/explorer/file-tree.tsx +235 -134
  261. package/src/web/components/explorer/flatten-visible-tree.ts +35 -40
  262. package/src/web/components/explorer/tree-node.tsx +69 -69
  263. package/src/web/components/explorer/use-tree-row-dnd.ts +3 -4
  264. package/src/web/components/git/git-status-panel.tsx +25 -0
  265. package/src/web/components/layout/draggable-tab.tsx +4 -2
  266. package/src/web/components/layout/nav-section-rail.tsx +12 -3
  267. package/src/web/components/layout/status-bar.tsx +10 -6
  268. package/src/web/components/layout/tab-content.tsx +5 -0
  269. package/src/web/components/layout/tab-pool.tsx +1 -0
  270. package/src/web/components/layout/upgrade-button.tsx +5 -1
  271. package/src/web/components/remote-desktop/remote-desktop-clipboard-client.ts +67 -0
  272. package/src/web/components/remote-desktop/remote-desktop-clipboard-notice.tsx +127 -0
  273. package/src/web/components/remote-desktop/remote-desktop-codec-labels.ts +28 -0
  274. package/src/web/components/remote-desktop/remote-desktop-custom-quality-dialog.tsx +144 -0
  275. package/src/web/components/remote-desktop/remote-desktop-mobile-key-bar.tsx +13 -2
  276. package/src/web/components/remote-desktop/remote-desktop-mobile-settings.tsx +232 -0
  277. package/src/web/components/remote-desktop/remote-desktop-mobile-toolbar.tsx +21 -8
  278. package/src/web/components/remote-desktop/remote-desktop-mobile-view.tsx +82 -3
  279. package/src/web/components/remote-desktop/remote-desktop-recording.ts +76 -0
  280. package/src/web/components/remote-desktop/remote-desktop-resolution-list.ts +100 -0
  281. package/src/web/components/remote-desktop/remote-desktop-toolbar.tsx +497 -0
  282. package/src/web/components/remote-desktop/remote-desktop-view-style.ts +145 -0
  283. package/src/web/components/remote-desktop/remote-desktop-window-content.tsx +126 -35
  284. package/src/web/components/remote-desktop/use-h264-canvas-decoder.ts +11 -0
  285. package/src/web/components/remote-desktop/use-opus-audio-player.ts +148 -0
  286. package/src/web/components/remote-desktop/use-remote-desktop-connection.ts +284 -1
  287. package/src/web/components/remote-desktop/use-remote-desktop-readiness.ts +75 -1
  288. package/src/web/components/remote-desktop/use-remote-desktop-recorder.ts +103 -0
  289. package/src/web/components/remote-desktop/use-remote-desktop-virtual-keyboard.ts +19 -2
  290. package/src/web/components/remote-desktop/use-remote-input-capture.ts +39 -3
  291. package/src/web/components/settings/theme-picker.tsx +2 -1
  292. package/src/web/components/ui/tooltip.tsx +15 -2
  293. package/src/web/hooks/use-delayed-flag.ts +35 -0
  294. package/src/web/lib/branch-review-state.ts +175 -0
  295. package/src/web/lib/file-icon-name.ts +62 -0
  296. package/src/web/lib/file-icons.generated.ts +3287 -25
  297. package/src/web/lib/file-icons.tsx +71 -73
  298. package/src/web/lib/flatten-expansions.ts +18 -5
  299. package/src/web/lib/message-ordinals.ts +23 -0
  300. package/src/web/lib/provider-logos.tsx +143 -0
  301. package/src/web/lib/tab-type-icons.ts +11 -0
  302. package/src/web/stores/file-store.ts +73 -35
  303. package/src/web/stores/panel-utils.ts +5 -0
  304. package/src/web/stores/project-framework-store.ts +82 -0
  305. package/src/web/stores/project-store.ts +7 -1
  306. package/src/web/stores/settings-store.ts +128 -0
  307. package/src/web/stores/tab-store.ts +1 -0
  308. package/src/web/stores/window-panel-persistence.ts +1 -0
  309. package/src/web/styles/file-icons.generated.css +3282 -234
  310. package/src/web/styles/globals.css +26 -0
  311. package/dist/web/assets/accounts-settings-section-BuxD9zH3.js +0 -1
  312. package/dist/web/assets/ai-settings-section-Dd9tOUp0.js +0 -1
  313. package/dist/web/assets/ai-settings-section-FrU2X8Fu.js +0 -1
  314. package/dist/web/assets/api-client-BUCo1lms.js +0 -1
  315. package/dist/web/assets/architecture-PBZL5I3N-5K0wEkAn.js +0 -1
  316. package/dist/web/assets/chat-tab-Cr6_KSGO.js +0 -15
  317. package/dist/web/assets/code-editor-DfmlGamv.js +0 -7
  318. package/dist/web/assets/context-menu-DOODKZdF.js +0 -1
  319. package/dist/web/assets/diff-viewer-BpNj7pN9.js +0 -4
  320. package/dist/web/assets/explorer-body-D-5z464e.js +0 -2
  321. package/dist/web/assets/explorer-window-content-7-1wuRul.js +0 -1
  322. package/dist/web/assets/file-icons-EzPL0q-k.js +0 -1
  323. package/dist/web/assets/file-icons.generated-CfaJuvZu.css +0 -1
  324. package/dist/web/assets/file-store-DMABxOkV.js +0 -1
  325. package/dist/web/assets/files-settings-section-CZzABuKz.js +0 -1
  326. package/dist/web/assets/git-log-panel-CRN4JVOx.js +0 -1
  327. package/dist/web/assets/gitGraph-HDMCJU4V-DkVT2MAM.js +0 -1
  328. package/dist/web/assets/group-chat-tab-DFa5mAx5.js +0 -1
  329. package/dist/web/assets/index-D7DbXD3G.css +0 -1
  330. package/dist/web/assets/index-ZtfxZOyb.js +0 -52
  331. package/dist/web/assets/info-3K5VOQVL-BBsmKGzA.js +0 -1
  332. package/dist/web/assets/keybindings-store-tVfhPANS.js +0 -1
  333. package/dist/web/assets/markdown-renderer-CvL1pWeK.js +0 -3
  334. package/dist/web/assets/markdown-renderer-sJNNVhlS.js +0 -1
  335. package/dist/web/assets/mobile-explorer-sheet-CZ0HZ2E_.js +0 -1
  336. package/dist/web/assets/notification-store-BZbsPnW0.js +0 -1
  337. package/dist/web/assets/packet-RMMSAZCW-DwmYPOlx.js +0 -1
  338. package/dist/web/assets/panel-store-BVLBRn6M.js +0 -1
  339. package/dist/web/assets/pie-UPGHQEXC-D0oogIFi.js +0 -1
  340. package/dist/web/assets/problems-panel-B7BWVhdy.js +0 -1
  341. package/dist/web/assets/project-store-BhMbVln3.js +0 -1
  342. package/dist/web/assets/radar-KQ55EAFF-CQIJOQGa.js +0 -1
  343. package/dist/web/assets/remote-desktop-mobile-sheet-ByjaeZKX.js +0 -2
  344. package/dist/web/assets/remote-desktop-mobile-view-D13mtr9F.js +0 -1
  345. package/dist/web/assets/remote-desktop-window-content-CbqTCaC6.js +0 -1
  346. package/dist/web/assets/settings-body-Rnu-FcX-.js +0 -2
  347. package/dist/web/assets/settings-store-DZPgPtEA.js +0 -2
  348. package/dist/web/assets/settings-tab-C6PHxs0Z.js +0 -1
  349. package/dist/web/assets/settings-window-content-CQ4gT2qj.js +0 -1
  350. package/dist/web/assets/system-monitor-body-2vQ1kMxE.js +0 -2
  351. package/dist/web/assets/system-monitor-tab-krAcvwF7.js +0 -1
  352. package/dist/web/assets/team-member-sheet-2FnRmk1a.js +0 -2
  353. package/dist/web/assets/tool-cards-DU_S2o51.js +0 -9
  354. package/dist/web/assets/treemap-KZPCXAKY-BhEtjljr.js +0 -1
  355. package/dist/web/assets/use-remote-desktop-display-choice-BK_f787P.js +0 -1
  356. package/dist/web/assets/video-preview-BDNwumbW.js +0 -1
  357. package/dist/web/assets/ws-auth-DEAJiGCR.js +0 -1
  358. /package/dist/web/assets/{abap-tkptwpsK.js → abap-IN2yINdN.js} +0 -0
  359. /package/dist/web/assets/{actionscript-3-Di9TMGwg.js → actionscript-3-C_t772eR.js} +0 -0
  360. /package/dist/web/assets/{ada-CB9F079D.js → ada-DPme3emm.js} +0 -0
  361. /package/dist/web/assets/{andromeeda-Dcl8jpb7.js → andromeeda-DSV4i4mZ.js} +0 -0
  362. /package/dist/web/assets/{apache-D0Nk0qsH.js → apache-CWr8s8I8.js} +0 -0
  363. /package/dist/web/assets/{apex-mQIoWncD.js → apex-Z20JnDF5.js} +0 -0
  364. /package/dist/web/assets/{applescript-C6ljbcsI.js → applescript-BKNPu3ga.js} +0 -0
  365. /package/dist/web/assets/{apply-theme-BwIMNXyX.js → apply-theme-CbR31K6I.js} +0 -0
  366. /package/dist/web/assets/{ara-WFqRPGPe.js → ara-hWdY3O96.js} +0 -0
  367. /package/dist/web/assets/{asciidoc-bNd8w4nf.js → asciidoc-eynhq496.js} +0 -0
  368. /package/dist/web/assets/{asm-BQ1_F580.js → asm-Bu02Kq4P.js} +0 -0
  369. /package/dist/web/assets/{aurora-x-Ciqs_Dbu.js → aurora-x-BHBGXbIZ.js} +0 -0
  370. /package/dist/web/assets/{awk-GCR-6KZV.js → awk-B4ojezyv.js} +0 -0
  371. /package/dist/web/assets/{ayu-dark-DxJXuj99.js → ayu-dark-D0GQzFi9.js} +0 -0
  372. /package/dist/web/assets/{ayu-light-jvSyJWDV.js → ayu-light-BVYb5Yn8.js} +0 -0
  373. /package/dist/web/assets/{ayu-mirage-Cg3BPO6H.js → ayu-mirage-BGFW6oNb.js} +0 -0
  374. /package/dist/web/assets/{ballerina-BPz4CrX6.js → ballerina-B6Og3IKT.js} +0 -0
  375. /package/dist/web/assets/{bat-CSTs2hIG.js → bat-D8YFILfx.js} +0 -0
  376. /package/dist/web/assets/{beancount-DMateAZX.js → beancount-BjlbBBuX.js} +0 -0
  377. /package/dist/web/assets/{berry-9lXg0GRM.js → berry-Cz1ygeoj.js} +0 -0
  378. /package/dist/web/assets/{bibtex-DIbqmvFg.js → bibtex-CVkJceJh.js} +0 -0
  379. /package/dist/web/assets/{bicep-02WGsoJ8.js → bicep-DrhYI5GX.js} +0 -0
  380. /package/dist/web/assets/{bird2-BY0tbu3k.js → bird2-BV-KJtmc.js} +0 -0
  381. /package/dist/web/assets/{c-jMBX_9Je.js → c-CuLlvxbG.js} +0 -0
  382. /package/dist/web/assets/{c3-B8JVbNjW.js → c3-CI7-1CJ0.js} +0 -0
  383. /package/dist/web/assets/{cadence-DUIf1tvL.js → cadence-DjxhNP3Q.js} +0 -0
  384. /package/dist/web/assets/{can-open-in-ppm-DGEc0s4P.js → can-open-in-ppm-cv5--yHX.js} +0 -0
  385. /package/dist/web/assets/{catppuccin-frappe-FhUCVxex.js → catppuccin-frappe-CEozpJRh.js} +0 -0
  386. /package/dist/web/assets/{catppuccin-latte-CzfrtmgL.js → catppuccin-latte-C4sXQFMc.js} +0 -0
  387. /package/dist/web/assets/{catppuccin-macchiato-6xL0lOtj.js → catppuccin-macchiato-Ci-fDhXy.js} +0 -0
  388. /package/dist/web/assets/{catppuccin-mocha-D39-xS3P.js → catppuccin-mocha-CH7YXw8d.js} +0 -0
  389. /package/dist/web/assets/{clarity-D8TeGvoT.js → clarity-BWrJ7UmT.js} +0 -0
  390. /package/dist/web/assets/{clojure-Dx_acSX7.js → clojure-C94fh2hb.js} +0 -0
  391. /package/dist/web/assets/{cmake-CyeYPq8o.js → cmake-CtvCkMsG.js} +0 -0
  392. /package/dist/web/assets/{codeowners-DrTI0lmI.js → codeowners-CJnubqzH.js} +0 -0
  393. /package/dist/web/assets/{codeql-CPkFxVW-.js → codeql-BOoP5HY2.js} +0 -0
  394. /package/dist/web/assets/{common-lisp-B0k-3LTj.js → common-lisp-B9nlMFJd.js} +0 -0
  395. /package/dist/web/assets/{coq-uygpo7to.js → coq-CgzIQiSH.js} +0 -0
  396. /package/dist/web/assets/{csharp-DKUYGfSp.js → csharp-4xAIx3jw.js} +0 -0
  397. /package/dist/web/assets/{css-DoOUp7v4.js → css-D4YoLYGn.js} +0 -0
  398. /package/dist/web/assets/{csv-B3YE13gi.js → csv-C2v-ZakW.js} +0 -0
  399. /package/dist/web/assets/{csv-parser-DXePqmXe.js → csv-parser-_oDBUkwQ.js} +0 -0
  400. /package/dist/web/assets/{cue-DViO2wvn.js → cue-Bbznh_an.js} +0 -0
  401. /package/dist/web/assets/{cypher-4BMCIW5N.js → cypher-BKhVEARF.js} +0 -0
  402. /package/dist/web/assets/{d-BT_sTHas.js → d-D6IhWopl.js} +0 -0
  403. /package/dist/web/assets/{dark-plus-DVVXu-1N.js → dark-plus-xAWu790O.js} +0 -0
  404. /package/dist/web/assets/{dart-DHz6f6_6.js → dart-Bj6S9O1j.js} +0 -0
  405. /package/dist/web/assets/{data-grid-types-OgUxMLdQ.js → data-grid-types-C5bthITf.js} +0 -0
  406. /package/dist/web/assets/{dax-ChYCjweK.js → dax-CF2zvaqq.js} +0 -0
  407. /package/dist/web/assets/{desktop-D6Lt4p04.js → desktop-CnmyxMKE.js} +0 -0
  408. /package/dist/web/assets/{diff-D8TDe4hP.js → diff-Qi8WuJsI.js} +0 -0
  409. /package/dist/web/assets/{dist-DOTlFQJA.js → dist-Bjseolz4.js} +0 -0
  410. /package/dist/web/assets/{dist-CrBOCtCW.js → dist-Czq7BUMy.js} +0 -0
  411. /package/dist/web/assets/{docker-Bsp0YbPx.js → docker-vLbbNP5d.js} +0 -0
  412. /package/dist/web/assets/{dotenv-B_7giSY_.js → dotenv-DJMvvNzn.js} +0 -0
  413. /package/dist/web/assets/{dracula-TrQL2iB5.js → dracula-CtAh2rhB.js} +0 -0
  414. /package/dist/web/assets/{dracula-soft-n2XOlgh-.js → dracula-soft-CXtuAAf6.js} +0 -0
  415. /package/dist/web/assets/{dream-maker-CdIEdhpT.js → dream-maker-D5YQyK49.js} +0 -0
  416. /package/dist/web/assets/{editor-font-BDrdiGf1.js → editor-font-ClTC9gaD.js} +0 -0
  417. /package/dist/web/assets/{emacs-lisp-CSbixs6u.js → emacs-lisp-CGTjbiqF.js} +0 -0
  418. /package/dist/web/assets/{everforest-dark-BuZahZYp.js → everforest-dark-D59p7vAQ.js} +0 -0
  419. /package/dist/web/assets/{everforest-light-DPH53z5H.js → everforest-light-B3Qtud3V.js} +0 -0
  420. /package/dist/web/assets/{fennel-B1-K_hlJ.js → fennel-BCyHNUwf.js} +0 -0
  421. /package/dist/web/assets/{fish-Cy12Dbua.js → fish-DDDlLV2G.js} +0 -0
  422. /package/dist/web/assets/{fluent-B_82ys8E.js → fluent-By54R9tl.js} +0 -0
  423. /package/dist/web/assets/{format-bytes-D5sBgIqU.js → format-bytes-D1AQyUCt.js} +0 -0
  424. /package/dist/web/assets/{fortran-free-form-BRQdNzZF.js → fortran-free-form-tP8mRym_.js} +0 -0
  425. /package/dist/web/assets/{gdscript-XBDmLUm9.js → gdscript-nHC2z6Te.js} +0 -0
  426. /package/dist/web/assets/{gdshader-DhVfB3u4.js → gdshader-K-cSmPlW.js} +0 -0
  427. /package/dist/web/assets/{genie-X3K29Y49.js → genie-BAcRhvEe.js} +0 -0
  428. /package/dist/web/assets/{gherkin-DKIx-6cX.js → gherkin-CcifxMLb.js} +0 -0
  429. /package/dist/web/assets/{github-dark-Bf7zdp5s.js → github-dark-67KKlMdB.js} +0 -0
  430. /package/dist/web/assets/{github-dark-default-DqsBBV11.js → github-dark-default-tja8pidR.js} +0 -0
  431. /package/dist/web/assets/{github-dark-dimmed-X2gqRz6w.js → github-dark-dimmed-CtjhYQtx.js} +0 -0
  432. /package/dist/web/assets/{github-dark-high-contrast-Z7tvbGX4.js → github-dark-high-contrast-Bf-LPFgn.js} +0 -0
  433. /package/dist/web/assets/{github-light-BKxt-7zZ.js → github-light-CbJxE7n3.js} +0 -0
  434. /package/dist/web/assets/{github-light-default-B0liF4d4.js → github-light-default-DRQmdHxt.js} +0 -0
  435. /package/dist/web/assets/{github-light-high-contrast-F4encJwr.js → github-light-high-contrast-CJkDgTeM.js} +0 -0
  436. /package/dist/web/assets/{gleam-Pz2bHgBB.js → gleam-D4LCl2ri.js} +0 -0
  437. /package/dist/web/assets/{gn-CgmdNsPU.js → gn-COY8zZmC.js} +0 -0
  438. /package/dist/web/assets/{gnuplot-qLNF5yZ9.js → gnuplot-ROk1VitR.js} +0 -0
  439. /package/dist/web/assets/{go-CSDUNhE7.js → go-H30c3p0C.js} +0 -0
  440. /package/dist/web/assets/{groovy-B3NEnOHF.js → groovy-DBLCaelV.js} +0 -0
  441. /package/dist/web/assets/{gruvbox-dark-hard-DcoftiUX.js → gruvbox-dark-hard-DJy4irps.js} +0 -0
  442. /package/dist/web/assets/{gruvbox-dark-medium-D1wVSuD8.js → gruvbox-dark-medium-Dhfw1QTO.js} +0 -0
  443. /package/dist/web/assets/{gruvbox-dark-soft-DWpunymH.js → gruvbox-dark-soft-BncJU-8w.js} +0 -0
  444. /package/dist/web/assets/{gruvbox-light-hard-rHMvyUdS.js → gruvbox-light-hard-Cq12_YvM.js} +0 -0
  445. /package/dist/web/assets/{gruvbox-light-medium-3O2KiXO7.js → gruvbox-light-medium-DqVja5_v.js} +0 -0
  446. /package/dist/web/assets/{gruvbox-light-soft-lNlXJPwH.js → gruvbox-light-soft-IvwhveGF.js} +0 -0
  447. /package/dist/web/assets/{haskell-aaG3aC0h.js → haskell-DA1JV-MC.js} +0 -0
  448. /package/dist/web/assets/{haxe-CPcHhUDd.js → haxe-V9RTRc5E.js} +0 -0
  449. /package/dist/web/assets/{hcl-CMQNlR_4.js → hcl-hr1lyZ10.js} +0 -0
  450. /package/dist/web/assets/{hjson-DeuklPFj.js → hjson-D3vW3i7X.js} +0 -0
  451. /package/dist/web/assets/{hlsl-BI3hlsaM.js → hlsl-BB85gfJT.js} +0 -0
  452. /package/dist/web/assets/{horizon-C7-gOcjs.js → horizon-DU8OOnj8.js} +0 -0
  453. /package/dist/web/assets/{horizon-bright-Cxt9bRW7.js → horizon-bright-DNMUclW6.js} +0 -0
  454. /package/dist/web/assets/{houston-DbF2G7iD.js → houston-DAiZKf6T.js} +0 -0
  455. /package/dist/web/assets/{hy-DRG6Hs2I.js → hy-C6Zv0LMe.js} +0 -0
  456. /package/dist/web/assets/{image-gallery-CObzTisL.js → image-gallery-CGvlv8zR.js} +0 -0
  457. /package/dist/web/assets/{imba-CxKv_2TW.js → imba-Didnp6Y-.js} +0 -0
  458. /package/dist/web/assets/{ini-BiHsyCdO.js → ini-m0T7q4gQ.js} +0 -0
  459. /package/dist/web/assets/{input-D-dR8RI6.js → input-C7Q8FxeL.js} +0 -0
  460. /package/dist/web/assets/{java-D3RZzgDB.js → java-Buy07jGn.js} +0 -0
  461. /package/dist/web/assets/{javascript-4FmSqG-T.js → javascript-Cjq3CbRL.js} +0 -0
  462. /package/dist/web/assets/{json-CjB236_E.js → json-BSVu8IqD.js} +0 -0
  463. /package/dist/web/assets/{json5-bkfO-Rco.js → json5-NN_Vb7e6.js} +0 -0
  464. /package/dist/web/assets/{jsonc-BwaTeLyv.js → jsonc-BdyZhBUw.js} +0 -0
  465. /package/dist/web/assets/{jsonl-CfxNSwGd.js → jsonl-BsDSNvwi.js} +0 -0
  466. /package/dist/web/assets/{jsonnet-CQETCYrp.js → jsonnet-Gm963F-U.js} +0 -0
  467. /package/dist/web/assets/{jssm-D5o3Zzdz.js → jssm-Ds3bKWgJ.js} +0 -0
  468. /package/dist/web/assets/{jsx-BAMILPLo.js → jsx-BT5mW58X.js} +0 -0
  469. /package/dist/web/assets/{kanagawa-dragon-BhGVxu3R.js → kanagawa-dragon-BvNvf7xd.js} +0 -0
  470. /package/dist/web/assets/{kanagawa-lotus-FH4Y59ro.js → kanagawa-lotus-CuSiliSn.js} +0 -0
  471. /package/dist/web/assets/{kanagawa-wave-eilvP9MB.js → kanagawa-wave-C6g-d6jT.js} +0 -0
  472. /package/dist/web/assets/{kdl-BQ-yvXUK.js → kdl-Bx4kZef-.js} +0 -0
  473. /package/dist/web/assets/{kotlin-BuCx0-VT.js → kotlin-qvaVvCcm.js} +0 -0
  474. /package/dist/web/assets/{kusto-DAN_n_Cy.js → kusto-BPLyNCN6.js} +0 -0
  475. /package/dist/web/assets/{laserwave-Cq2BlxrT.js → laserwave-Sf8ZEhWP.js} +0 -0
  476. /package/dist/web/assets/{lean-Dhq7uXKw.js → lean-DGe6uqgk.js} +0 -0
  477. /package/dist/web/assets/{less-CxaMQb8j.js → less-CqM85x-9.js} +0 -0
  478. /package/dist/web/assets/{lib-BVwmtv2i.js → lib-CD6pOS4u.js} +0 -0
  479. /package/dist/web/assets/{libesm-DdtEjhIi.js → libesm-CPh1Av0r.js} +0 -0
  480. /package/dist/web/assets/{light-plus-B4sMAend.js → light-plus-DBtKwhFA.js} +0 -0
  481. /package/dist/web/assets/{llvm-DQ6-B_6Y.js → llvm-Dpbq3gky.js} +0 -0
  482. /package/dist/web/assets/{log-Bb_vqvu7.js → log-qKMcPm9S.js} +0 -0
  483. /package/dist/web/assets/{logo-BzfYv3A_.js → logo-DFo7i5KH.js} +0 -0
  484. /package/dist/web/assets/{luau-BnAeNR77.js → luau-DGlqWFyL.js} +0 -0
  485. /package/dist/web/assets/{make-B6IFuxpC.js → make-CixdvUQe.js} +0 -0
  486. /package/dist/web/assets/{markdown-Z0xi77Fn.js → markdown-CmRnbngE.js} +0 -0
  487. /package/dist/web/assets/{material-theme-1scy1SYn.js → material-theme-WDUbafB-.js} +0 -0
  488. /package/dist/web/assets/{material-theme-darker-G-JykXQP.js → material-theme-darker-u8y2xpOk.js} +0 -0
  489. /package/dist/web/assets/{material-theme-lighter-BtKkCMVq.js → material-theme-lighter-cyQZK3Tg.js} +0 -0
  490. /package/dist/web/assets/{material-theme-ocean-irrDC89L.js → material-theme-ocean-LBAVvw7k.js} +0 -0
  491. /package/dist/web/assets/{material-theme-palenight-Eq5_K4LM.js → material-theme-palenight-BJqhBxL0.js} +0 -0
  492. /package/dist/web/assets/{matlab-BE3oCXZC.js → matlab-B38sOJee.js} +0 -0
  493. /package/dist/web/assets/{mdx-DYiuFTGw.js → mdx-C8QrE_IW.js} +0 -0
  494. /package/dist/web/assets/{mermaid-DVlaEvtw.js → mermaid-BQ-eeU8A.js} +0 -0
  495. /package/dist/web/assets/{min-dark-DSkeTpQa.js → min-dark-D4gNa5tv.js} +0 -0
  496. /package/dist/web/assets/{min-light-CFv2Q0uF.js → min-light-Dfbhn_PL.js} +0 -0
  497. /package/dist/web/assets/{mipsasm-CiAFuZQ2.js → mipsasm-bLOhJkdm.js} +0 -0
  498. /package/dist/web/assets/{mojo-ARshcaSQ.js → mojo-B0BCOMQW.js} +0 -0
  499. /package/dist/web/assets/{monaco-dotenv-language-BDYnKcnr.js → monaco-dotenv-language-uB-wok6C.js} +0 -0
  500. /package/dist/web/assets/{monokai-CPTV-SOg.js → monokai-Bdw_07OM.js} +0 -0
  501. /package/dist/web/assets/{moonbit-D57B0dZr.js → moonbit-BFdGaeuk.js} +0 -0
  502. /package/dist/web/assets/{move-qgy3G_o9.js → move-CyXQFGPM.js} +0 -0
  503. /package/dist/web/assets/{narrat-CZ3rC-oc.js → narrat-9DvTD3_3.js} +0 -0
  504. /package/dist/web/assets/{nextflow-groovy-BxxIOcI4.js → nextflow-groovy-C4VY3kNY.js} +0 -0
  505. /package/dist/web/assets/{night-owl-B1ryGCda.js → night-owl-feYm66V0.js} +0 -0
  506. /package/dist/web/assets/{night-owl-light-BZvNnvMF.js → night-owl-light-DALqFjvx.js} +0 -0
  507. /package/dist/web/assets/{nix-BVBQi8yv.js → nix-C1kPA9Sw.js} +0 -0
  508. /package/dist/web/assets/{nord-3NXw613h.js → nord-1dvrj1Al.js} +0 -0
  509. /package/dist/web/assets/{nushell-BMOLRalb.js → nushell-DSQ_9Uqj.js} +0 -0
  510. /package/dist/web/assets/{objective-c-AxruEUgh.js → objective-c-Dhcn5ZK8.js} +0 -0
  511. /package/dist/web/assets/{objective-cpp-cFT0qRsD.js → objective-cpp-CGVYCSrp.js} +0 -0
  512. /package/dist/web/assets/{ocaml-_8JH6O8J.js → ocaml-rxXW21An.js} +0 -0
  513. /package/dist/web/assets/{odin-nwYvR0O6.js → odin-ClSJX2DE.js} +0 -0
  514. /package/dist/web/assets/{one-dark-pro-C1LaYJaH.js → one-dark-pro-3gbWDfFW.js} +0 -0
  515. /package/dist/web/assets/{one-light-B7rMc__c.js → one-light-C2q5ZZAw.js} +0 -0
  516. /package/dist/web/assets/{openscad-Cwes8bKO.js → openscad-eHXeafBl.js} +0 -0
  517. /package/dist/web/assets/{pascal-DMt-6tdZ.js → pascal-kKha3Txs.js} +0 -0
  518. /package/dist/web/assets/{pkl-QBTyYQmB.js → pkl-CTiPytg-.js} +0 -0
  519. /package/dist/web/assets/{plastic-BTSUp8Fk.js → plastic-D1Ygezi4.js} +0 -0
  520. /package/dist/web/assets/{plsql-DQv3IFXP.js → plsql-D_gbgZIr.js} +0 -0
  521. /package/dist/web/assets/{po-CNwZx4tW.js → po-BkhbDUnq.js} +0 -0
  522. /package/dist/web/assets/{poimandres-8Os7Qequ.js → poimandres-Do6NywrA.js} +0 -0
  523. /package/dist/web/assets/{polar-b4yK8oD8.js → polar-B3B23wca.js} +0 -0
  524. /package/dist/web/assets/{postcss-sFctwpxx.js → postcss-Dv_YBvWd.js} +0 -0
  525. /package/dist/web/assets/{powerquery-Cg_WrtuN.js → powerquery-fKJWI-Sh.js} +0 -0
  526. /package/dist/web/assets/{powershell-C7Ifz95h.js → powershell-B4Y7KIgx.js} +0 -0
  527. /package/dist/web/assets/{prisma-Z52HUJzQ.js → prisma-J--XdTXX.js} +0 -0
  528. /package/dist/web/assets/{prolog-DLqW-zR1.js → prolog-BBTg4Vb3.js} +0 -0
  529. /package/dist/web/assets/{proto-BOIzxCCJ.js → proto-DWlum3T8.js} +0 -0
  530. /package/dist/web/assets/{puppet-De7TjZIf.js → puppet-ZiUfc9JV.js} +0 -0
  531. /package/dist/web/assets/{purescript-R76ou1WT.js → purescript-CmT8SCHB.js} +0 -0
  532. /package/dist/web/assets/{python-Dprm0tNP.js → python-BURpj-fq.js} +0 -0
  533. /package/dist/web/assets/{qmldir-Clp_jpNw.js → qmldir-CpaiJywQ.js} +0 -0
  534. /package/dist/web/assets/{qss-eQPYYzSo.js → qss-C-zdz0i5.js} +0 -0
  535. /package/dist/web/assets/{r-CCcRHIf5.js → r-K56BeS-s.js} +0 -0
  536. /package/dist/web/assets/{racket-CzE472yp.js → racket-gkn6Z_Jw.js} +0 -0
  537. /package/dist/web/assets/{raku-DQwXrTSi.js → raku-C0ZBuKlx.js} +0 -0
  538. /package/dist/web/assets/{red-KCS6wyop.js → red-BWZ6x-3p.js} +0 -0
  539. /package/dist/web/assets/{reg-CcrAddUh.js → reg-Dt_EsyGR.js} +0 -0
  540. /package/dist/web/assets/{regexp-DDbud-Ri.js → regexp-CT6BLG2C.js} +0 -0
  541. /package/dist/web/assets/{rel-99GDgaHp.js → rel-MJ2LcyzJ.js} +0 -0
  542. /package/dist/web/assets/{resolve-theme-CW2niGV2.js → resolve-theme-B_ukrg_8.js} +0 -0
  543. /package/dist/web/assets/{riscv-Dm2eNv0R.js → riscv-BZYWzMAA.js} +0 -0
  544. /package/dist/web/assets/{ron-CzFt0ygH.js → ron-CLbwHWMn.js} +0 -0
  545. /package/dist/web/assets/{rose-pine-Ck6lCrCP.js → rose-pine-BPiI_daR.js} +0 -0
  546. /package/dist/web/assets/{rose-pine-dawn-vtALRJNj.js → rose-pine-dawn-BjkJc7dW.js} +0 -0
  547. /package/dist/web/assets/{rose-pine-moon-DPto8qXw.js → rose-pine-moon-BQSxkt7g.js} +0 -0
  548. /package/dist/web/assets/{rosmsg-NtS6429H.js → rosmsg-yt4151pJ.js} +0 -0
  549. /package/dist/web/assets/{rust-BjIXvF9h.js → rust-1dAcengL.js} +0 -0
  550. /package/dist/web/assets/{sass-CQ23wf5E.js → sass-DUgtQJsg.js} +0 -0
  551. /package/dist/web/assets/{scala-LfKLM143.js → scala-CxoGYpS0.js} +0 -0
  552. /package/dist/web/assets/{scheme-D3SLBXlF.js → scheme-B2K3u9e3.js} +0 -0
  553. /package/dist/web/assets/{sdbl-ODRd2rGN.js → sdbl-C0Ygk0n6.js} +0 -0
  554. /package/dist/web/assets/{shellscript-3KB1G_oA.js → shellscript-C2CvhqMk.js} +0 -0
  555. /package/dist/web/assets/{slack-dark-DTtyWmIy.js → slack-dark-DYdgzRGj.js} +0 -0
  556. /package/dist/web/assets/{slack-ochin-Bu0PApk3.js → slack-ochin-X-WD0WCg.js} +0 -0
  557. /package/dist/web/assets/{smalltalk-haMT-Sbe.js → smalltalk-Dp7g_zb9.js} +0 -0
  558. /package/dist/web/assets/{snazzy-light-DXJzefE9.js → snazzy-light-DWHWr-a6.js} +0 -0
  559. /package/dist/web/assets/{solarized-dark-B6LyYa18.js → solarized-dark-CxdLsjvb.js} +0 -0
  560. /package/dist/web/assets/{solarized-light-QLADebkv.js → solarized-light-Cz9rAvG5.js} +0 -0
  561. /package/dist/web/assets/{solidity-mzfq1aDc.js → solidity-D33gHHdn.js} +0 -0
  562. /package/dist/web/assets/{splunk-CyH4RWBp.js → splunk-BFbOroYw.js} +0 -0
  563. /package/dist/web/assets/{sql-CWVlT5D3.js → sql-ChpxvmdY.js} +0 -0
  564. /package/dist/web/assets/{ssh-config-DNvQJ1Qx.js → ssh-config-CUuSQ6JG.js} +0 -0
  565. /package/dist/web/assets/{streaming-store-uDu3PoN1.js → streaming-store-BYukdB56.js} +0 -0
  566. /package/dist/web/assets/{stylus-ByQQdtmf.js → stylus-DDihP0_W.js} +0 -0
  567. /package/dist/web/assets/{swift-D1cpY4ow.js → swift-XoalUn4x.js} +0 -0
  568. /package/dist/web/assets/{synthwave-84-BDF_-QiW.js → synthwave-84-DjkG0Bff.js} +0 -0
  569. /package/dist/web/assets/{system-verilog-BYrkMydC.js → system-verilog-f_pLKMYd.js} +0 -0
  570. /package/dist/web/assets/{systemd-CQm3zyK0.js → systemd-DjDcLfxp.js} +0 -0
  571. /package/dist/web/assets/{talonscript-DMW5CzE1.js → talonscript-BpUzYcDl.js} +0 -0
  572. /package/dist/web/assets/{tasl-BhFzMCr2.js → tasl-DP-cUbg8.js} +0 -0
  573. /package/dist/web/assets/{tcl-D7VEAF-9.js → tcl-BHoy-dfE.js} +0 -0
  574. /package/dist/web/assets/{terraform-DdT5A0Vy.js → terraform-vCVxN25B.js} +0 -0
  575. /package/dist/web/assets/{tokyo-night-WGLMM6Q9.js → tokyo-night-D0u4xZba.js} +0 -0
  576. /package/dist/web/assets/{toml-CZ7ipXHZ.js → toml-BS8D0__A.js} +0 -0
  577. /package/dist/web/assets/{tsv-B1zVvoTr.js → tsv-DCb-NQh3.js} +0 -0
  578. /package/dist/web/assets/{tsx-D4HwRUBq.js → tsx-DF8YeRgG.js} +0 -0
  579. /package/dist/web/assets/{turtle-B4r8KEJH.js → turtle-Bhx1GlIy.js} +0 -0
  580. /package/dist/web/assets/{typescript-BnHhxMtp.js → typescript-CAVD2Wb6.js} +0 -0
  581. /package/dist/web/assets/{typespec-DP2q46GT.js → typespec-D7mchdY_.js} +0 -0
  582. /package/dist/web/assets/{typst-DFsD_yaD.js → typst-CeXzq6Yw.js} +0 -0
  583. /package/dist/web/assets/{use-is-mobile-BQ9Bv5Ga.js → use-is-mobile-TkcwP1Gx.js} +0 -0
  584. /package/dist/web/assets/{use-visual-viewport-CbwnktSG.js → use-visual-viewport-Lp878uzs.js} +0 -0
  585. /package/dist/web/assets/{v-Bji8hSVT.js → v-DhzO54je.js} +0 -0
  586. /package/dist/web/assets/{vala-D3TIgGmU.js → vala-Dw_RAk3E.js} +0 -0
  587. /package/dist/web/assets/{vb-DvcxFS2q.js → vb-CiXdNh2Y.js} +0 -0
  588. /package/dist/web/assets/{vendor-xterm-BqPDsE9b.js → vendor-xterm-BHEY6Ctu.js} +0 -0
  589. /package/dist/web/assets/{verilog-Dy7cF35d.js → verilog-fITdxqwV.js} +0 -0
  590. /package/dist/web/assets/{vesper-BzjwUv9J.js → vesper-B1wm_At7.js} +0 -0
  591. /package/dist/web/assets/{vhdl-DuFnFNmW.js → vhdl-ebHjqKp9.js} +0 -0
  592. /package/dist/web/assets/{viml-yAPgKlbz.js → viml-Dgmnoxoo.js} +0 -0
  593. /package/dist/web/assets/{vitesse-black-oSSQdyIZ.js → vitesse-black-BZ5gMb7l.js} +0 -0
  594. /package/dist/web/assets/{vitesse-dark-BZxwPihB.js → vitesse-dark-Lij7kkOt.js} +0 -0
  595. /package/dist/web/assets/{vitesse-light-uIigycvt.js → vitesse-light-DW6Wq7VC.js} +0 -0
  596. /package/dist/web/assets/{vyper-CX3NAxm5.js → vyper-6FxDnVNg.js} +0 -0
  597. /package/dist/web/assets/{wasm-6mEOOvVa.js → wasm-BDxaNPCj.js} +0 -0
  598. /package/dist/web/assets/{wasm-D9td37qO.js → wasm-DKhAtzfe.js} +0 -0
  599. /package/dist/web/assets/{wenyan-lwNIHgk6.js → wenyan-C0k0DYG_.js} +0 -0
  600. /package/dist/web/assets/{wgsl-DQAzCfog.js → wgsl-Y2zagbdn.js} +0 -0
  601. /package/dist/web/assets/{wikitext-DTXmt5NE.js → wikitext-C4QGWzkY.js} +0 -0
  602. /package/dist/web/assets/{wit-Bdw_7WDH.js → wit-CQESzjHy.js} +0 -0
  603. /package/dist/web/assets/{wolfram-DqyHDZ07.js → wolfram-B-ia69Ia.js} +0 -0
  604. /package/dist/web/assets/{yaml-CPXhdByd.js → yaml-CEYYTQPm.js} +0 -0
  605. /package/dist/web/assets/{zenscript-6s_i5zek.js → zenscript-BggWXIeJ.js} +0 -0
  606. /package/dist/web/assets/{zig-B2htezJ_.js → zig-CbaUfBQe.js} +0 -0
@@ -1,68 +1,83 @@
1
1
  /**
2
2
  * Vendor the vscode-icons file icon theme into a module the browser can use.
3
3
  *
4
- * The icons are the real ones from `vscode-icons-team.vscode-icons`, taken from
5
- * the Iconify collection generated out of that extension, and the *mapping* is
6
- * the extension's own (`vscode-icons-js`) rather than a hand-written guess — so
7
- * `.spec.ts` gets the test glyph, `go.sum` gets the Go one, and a filename with
8
- * a dedicated icon (`package.json`, `Dockerfile`, `.prettierrc`) gets it.
4
+ * Both halves come from `vscode-icons-team.vscode-icons` itself: the artwork
5
+ * from the Iconify collection generated out of that extension, and the
6
+ * *mapping* from the extension's own `src/iconsManifest/` pinned, digested
7
+ * and re-implemented below exactly as `manifestBuilder.ts` builds it.
9
8
  *
10
- * Both of those are **build-time** dependencies. Resolving names at run time
11
- * would mean shipping the extension's four lookup tables (77 KB of JS) and,
12
- * worse, the 1595 icon bodies (about 1.3 MB) to draw a tree that shows perhaps
13
- * forty distinct glyphs. So this script asks the tables which icon each name
14
- * PPM cares about resolves to, and emits only those bodies.
9
+ * The mapping used to come from `vscode-icons-js` alone, a third-party
10
+ * repackaging, and that is the whole reason this file was rewritten. It is
11
+ * versioned apart from the extension and lags it badly: its table of
12
+ * *multi-dot* extensions held **41** entries against the real manifest's 1006,
13
+ * so `app.controller.ts`, `app.module.ts` and `app.service.ts` all drew the
14
+ * plain TypeScript glyph while the Nest artwork sat unused in the bundle. It
15
+ * also knows nothing of the manifest's glob form (`babel.config.{js,cjs,mjs,json}`)
16
+ * or of the presets, so every gap had to be patched by hand in an `OVERRIDES`
17
+ * table that only ever grew.
15
18
  *
16
- * The coverage list below is deliberately generous but finite: anything not on
17
- * it falls back to the theme's own `default-file`, which is what the extension
18
- * itself does for an unknown type. Add a line and re-run:
19
+ * It is still read, second, and that is not laziness the two sources are
20
+ * complementary in a way worth stating. The manifest associates a great many
21
+ * types by *VS Code language id* and nothing else: the `dotenv` entry has
22
+ * `extensions: []`, and `.cc`, `.htm`, `.conf`, `.kts` and twenty more reach
23
+ * their icon only because VS Code already knows which language owns them.
24
+ * `languages.ts` carries one `knownExtensions` list per language and it is the
25
+ * short one, so a manifest-only port silently drops those — measured, against
26
+ * the previous tables: 36 names that had an icon stopped having one. So the
27
+ * manifest wins wherever it answers, `vscode-icons-js` fills the rest, and
28
+ * `LANGUAGE_EXTRAS` holds the handful neither knows.
29
+ *
30
+ * Both are **build-time** dependencies. Resolving names at run time would mean
31
+ * shipping the manifest itself (200 KB of TypeScript) and the lookup code with
32
+ * it, to answer a question whose answer never changes between releases.
33
+ *
34
+ * The outputs are committed. Re-run after bumping `MANIFEST_VERSION`:
19
35
  *
20
36
  * bun scripts/gen-file-icons.ts
21
37
  */
22
- import { getIconForFile, getIconForFolder, getIconForOpenFolder } from "vscode-icons-js";
23
- // Reaching past the entry point on purpose: this table is the set of *double*
24
- // extensions the theme knows (`spec.ts`, `d.ts`, `stories.tsx`, `js.map`), and
25
- // those are the glyphs a TypeScript repository shows most after `.ts` itself.
26
- // Hand-listing them would drift; if the path ever moves, this script fails
27
- // loudly at generation time rather than quietly emitting worse icons.
28
- import { FileExtensions2ToIcon } from "vscode-icons-js/dist/generated/FileExtensions2ToIcon";
29
- import { writeFileSync } from "node:fs";
38
+ import { createHash } from "node:crypto";
39
+ import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
40
+ import { tmpdir } from "node:os";
30
41
  import { resolve } from "node:path";
31
42
  import { writeNotices } from "./third-party-notices.ts";
32
43
 
33
44
  const OUT_TS = resolve(import.meta.dir, "../src/web/lib/file-icons.generated.ts");
34
45
  const OUT_CSS = resolve(import.meta.dir, "../src/web/styles/file-icons.generated.css");
35
46
 
47
+ /**
48
+ * Pinned, and checked against a digest rather than trusted.
49
+ *
50
+ * These are the three files the extension builds its own icon theme from. A
51
+ * branch can be force-pushed and a tag moved, so "fetch master" would make the
52
+ * committed tables unreproducible — and a mapping that changed under us moves
53
+ * icons all over the tree with nothing to point at. Bump the tag, run the
54
+ * script, and it will print the digests it actually got.
55
+ */
56
+ const MANIFEST_VERSION = "v12.19.0";
57
+ const MANIFEST_FILES: Record<string, string> = {
58
+ supportedExtensions: "277b81ecebe527dea6deedf39b891d9746cc58b14dd2e311bc2da0bf2ff8eee6",
59
+ supportedFolders: "8b638f423efdcbeabbc5ede863db7d54ee6738da2eece29cf07833a627a12685",
60
+ languages: "43542118e7438a0c5f31b1c48c085c6b1b8079b2902e241664c4cff1e4c8568e",
61
+ };
62
+
36
63
  /**
37
64
  * A glyph bigger than this is dropped, and whatever asked for it falls back to
38
- * the neutral default.
65
+ * the next shorter suffix — `.controller.ts` to `.ts`, and past that to the
66
+ * neutral default.
39
67
  *
40
- * The set is extremely skewed the median body is ~1 KB and `file-type-composer`
68
+ * The set is extremely skewed: the median body is ~1 KB and `file-type-composer`
41
69
  * alone is 83 KB, because some of these are detailed illustrations rather than
42
70
  * icons. At the 16px a file tree draws them at, that detail is invisible; all it
43
- * costs is download. `KEEP_ANY_SIZE` exempts the few worth paying for anyway.
71
+ * costs is download.
44
72
  *
45
73
  * 5000 rather than something tighter because the distribution has no useful gap
46
74
  * below it, and a tight budget cuts by weight rather than by worth: at 1800,
47
75
  * `.tsx` (`file-type-reactts`, 1833 bytes — 33 over) drew the blank-page default,
48
76
  * and so did `.jsx` (1833), `.go` (1825) and `.rs` (3957). Everything above 5000
49
- * really is an illustration. The 39 extra glyphs cost 25 KiB brotli.
77
+ * really is an illustration. `KEEP_ANY_SIZE` exempts the few worth paying for.
50
78
  */
51
79
  const MAX_BODY_BYTES = 5000;
52
80
 
53
- /**
54
- * Keys the mapping names that the collection spells differently.
55
- *
56
- * vscode-icons revises a glyph by publishing it under a numbered name, and the
57
- * two packages are versioned apart — so the mapping can hand back a name the
58
- * collection no longer has. The script reports every unresolved key rather than
59
- * dropping it quietly, and this is where the answer goes. `Makefile` has no
60
- * glyph in the collection at all and falls back to the default on purpose.
61
- */
62
- const ALIASES: Record<string, string> = {
63
- "file-type-pdf": "file-type-pdf2",
64
- };
65
-
66
81
  /** Languages common enough to keep whatever they weigh. */
67
82
  const KEEP_ANY_SIZE = new Set(
68
83
  `file-type-ruby file-type-perl file-type-groovy file-type-maven file-type-http
@@ -72,148 +87,199 @@ const KEEP_ANY_SIZE = new Set(
72
87
  );
73
88
 
74
89
  /**
75
- * What the mapping should have said.
90
+ * Keys the manifest names that the collection spells differently.
91
+ *
92
+ * vscode-icons revises a glyph by publishing it under a numbered name, and the
93
+ * artwork package is versioned apart from the extension — so the manifest can
94
+ * name an icon the collection no longer has. Every unresolved name is reported
95
+ * at the end of a run rather than dropped quietly, and this is where the answer
96
+ * goes.
97
+ */
98
+ const ALIASES: Record<string, string> = {
99
+ "file-type-pdf": "file-type-pdf2",
100
+ };
101
+
102
+ /**
103
+ * What neither source knows, because VS Code answers it from somewhere else.
76
104
  *
77
- * `vscode-icons-js` is versioned apart from the artwork and its tables are the
78
- * older half: the collection has `file-type-bun`, `file-type-dotenv`,
79
- * `file-type-bazel` and thirty more that no table points at, so `Dockerfile`,
80
- * `bun.lock`, `.env.local`, `Rakefile` and every `.mts` file drew the blank-page
81
- * default while the right glyph sat unused in the bundle. A name here wins over
82
- * whatever the mapping answers — including where the mapping answers *wrongly*,
83
- * which is `bunfig.toml` getting the generic TOML glyph.
105
+ * All of these resolve in VS Code through a language association contributed by
106
+ * a *different* extension or by a built-in grammar, which is a table neither the
107
+ * icon manifest nor `vscode-icons-js` contains. `.env.local` is the one that
108
+ * matters most here the manifest's `dotenv` entry declares no extensions at
109
+ * all and leans entirely on the `dotenv` language id, so every qualified env
110
+ * file drew a blank page.
84
111
  *
85
- * Every value is checked against the collection at generation time, so a key
86
- * that stops existing is reported rather than silently drawing nothing. Some
87
- * names are deliberately absent: `Makefile` has no glyph at all, and `LICENSE`
88
- * (23 KB) and `file.pug` (6 KB) are over the budget below.
112
+ * Keys are matched the way the runtime matches: whole filename first, then a
113
+ * dotted suffix. Values are checked against the collection at generation time,
114
+ * so an entry that stops resolving is reported rather than silently dropped.
89
115
  */
90
- const OVERRIDES: Record<string, string> = {
91
- // Bun, which is what this project is built with.
92
- "bun.lock": "file-type-bun",
93
- "bun.lockb": "file-type-bun",
94
- "bunfig.toml": "file-type-bunfig",
95
- // Containers.
96
- Dockerfile: "file-type-docker",
97
- dockerfile: "file-type-docker",
98
- ".dockerignore": "file-type-docker",
99
- Vagrantfile: "file-type-vagrant",
100
- // Environment files, which the tables know only as a bare `.env`.
101
- ".env.local": "file-type-dotenv",
102
- ".env.development": "file-type-dotenv",
103
- ".env.production": "file-type-dotenv",
104
- ".env.example": "file-type-dotenv",
105
- ".env.test": "file-type-dotenv",
106
- // Build systems.
107
- "meson.build": "file-type-meson",
108
- BUILD: "file-type-bazel",
109
- WORKSPACE: "file-type-bazel",
110
- Justfile: "file-type-just",
111
- Procfile: "file-type-procfile",
112
- gradlew: "file-type-gradle",
113
- "build.gradle.kts": "file-type-gradle",
114
- // Per-language lockfiles and manifests.
115
- "Cargo.lock": "file-type-rust",
116
- "go.work": "file-type-go-package",
117
- Gemfile: "file-type-ruby",
118
- "Gemfile.lock": "file-type-ruby",
119
- Rakefile: "file-type-rake",
120
- Pipfile: "file-type-python",
121
- "Pipfile.lock": "file-type-python",
122
- "poetry.lock": "file-type-poetry",
123
- "pytest.ini": "file-type-pytest",
124
- "mix.exs": "file-type-elixir",
125
- // Extensions the tables never learned.
126
- mts: "file-type-typescript",
127
- cts: "file-type-typescript",
128
- cjs: "file-type-js",
129
- jsonl: "file-type-json",
130
- pyi: "file-type-python",
131
- kts: "file-type-kotlin",
132
- exs: "file-type-elixir",
133
- ini: "file-type-ini",
134
- graphql: "file-type-graphql",
135
- proto: "file-type-protobuf",
136
- tif: "file-type-image",
137
- rest: "file-type-rest",
138
- tfvars: "file-type-terraform",
139
- npmrc: "file-type-npm",
140
- nvmrc: "file-type-node",
141
- apk: "file-type-binary",
142
- deb: "file-type-binary",
143
- rpm: "file-type-binary",
116
+ const LANGUAGE_EXTRAS: { names: Record<string, string>; extensions: Record<string, string> } = {
117
+ names: {
118
+ // Bazel, whose two marker files have no extension at all.
119
+ build: "bazel",
120
+ workspace: "bazel",
121
+ // Wrapper scripts and pipeline definitions named after their tool.
122
+ gradlew: "gradle",
123
+ "gradlew.bat": "gradle",
124
+ jenkinsfile: "groovy",
125
+ "cargo.lock": "rust",
126
+ // `file-type-bundler` is 43 KB an illustration, not an icon, so the
127
+ // budget drops it and Ruby's own glyph is the honest stand-in.
128
+ gemfile: "ruby",
129
+ "gemfile.lock": "ruby",
130
+ },
131
+ extensions: {
132
+ // Languages and formats VS Code associates through a grammar neither source
133
+ // enumerates. Each of these was a line in the old hand-written `OVERRIDES`
134
+ // and drew a blank page without it.
135
+ graphql: "graphql",
136
+ gql: "graphql",
137
+ kts: "kotlin",
138
+ exs: "elixir",
139
+ tif: "image",
140
+ rest: "rest",
141
+ apk: "binary",
142
+ rpm: "binary",
143
+ npmrc: "npm",
144
+ nvmrc: "node",
145
+ // `.env.local`, `.env.production`, `.env.test.local` — the manifest has the
146
+ // artwork and reaches it only by language id.
147
+ "env.local": "dotenv",
148
+ "env.development": "dotenv",
149
+ "env.production": "dotenv",
150
+ "env.staging": "dotenv",
151
+ "env.test": "dotenv",
152
+ "env.example": "dotenv",
153
+ "env.sample": "dotenv",
154
+ },
144
155
  };
145
156
 
146
- /** Extensions worth a glyph of their own. */
147
- const EXTENSIONS = `
148
- ts mts cts tsx js mjs cjs jsx json jsonc json5 jsonl map
149
- html htm ejs hbs handlebars pug css scss sass less styl vue svelte astro
150
- go py pyi rb php java jar kt kts scala rs c h cpp cc hpp cs swift
151
- dart lua pl r ex exs clj groovy gradle vb vbs ps1 bat cmd
152
- sh bash zsh fish
153
- yml yaml toml ini cfg conf env properties xml plist csv tsv
154
- xls xlsx doc docx pdf
155
- sql db sqlite sqlite3 prisma graphql gql proto
156
- png jpg jpeg gif svg webp avif ico bmp tif tiff
157
- mp4 webm mov avi mkv mp3 wav ogg flac m4a
158
- woff woff2 ttf otf eot
159
- zip tar gz tgz bz2 xz 7z rar exe dll so bin wasm apk deb rpm iso img dmg
160
- md mdx txt log lock diff patch key pem crt cert
161
- http rest ipynb tf tfvars nix cmake mk dockerfile
162
- tex sln csproj rake gemspec webmanifest babelrc npmrc nvmrc
163
- `.trim().split(/\s+/);
164
-
165
- /** Whole filenames the theme gives a dedicated glyph. */
166
- const FILENAMES = `
167
- package.json package-lock.json bun.lock bun.lockb yarn.lock pnpm-lock.yaml
168
- tsconfig.json jsconfig.json bunfig.toml deno.json
169
- .gitignore .gitattributes .gitmodules .gitkeep .mailmap
170
- .npmrc .npmignore .nvmrc .yarnrc .editorconfig .browserslistrc
171
- .prettierrc .prettierrc.json .prettierignore
172
- .eslintrc .eslintrc.js .eslintrc.json eslint.config.js eslint.config.mjs
173
- .env .env.local .env.development .env.production .env.example .env.test
174
- Dockerfile dockerfile docker-compose.yml docker-compose.yaml compose.yaml .dockerignore
175
- Makefile makefile CMakeLists.txt meson.build BUILD WORKSPACE Justfile
176
- README.md readme.md LICENSE LICENSE.md COPYING CHANGELOG.md CONTRIBUTING.md
177
- CODE_OF_CONDUCT.md SECURITY.md CODEOWNERS AUTHORS
178
- vite.config.ts vite.config.js rollup.config.js webpack.config.js
179
- tailwind.config.js tailwind.config.ts postcss.config.js postcss.config.mjs
180
- babel.config.js .babelrc jest.config.js jest.config.ts vitest.config.ts
181
- playwright.config.ts cypress.config.ts karma.conf.js nodemon.json pm2.config.js
182
- next.config.js next.config.mjs nuxt.config.ts svelte.config.js astro.config.mjs
183
- angular.json vue.config.js remix.config.js gatsby-config.js metro.config.js
184
- go.mod go.sum go.work Cargo.toml Cargo.lock
185
- requirements.txt pyproject.toml setup.py setup.cfg Pipfile Pipfile.lock poetry.lock
186
- tox.ini pytest.ini manage.py
187
- Gemfile Gemfile.lock Rakefile composer.json composer.lock
188
- pom.xml build.gradle build.gradle.kts settings.gradle gradlew mix.exs
189
- .gitlab-ci.yml .travis.yml appveyor.yml azure-pipelines.yml Jenkinsfile
190
- netlify.toml vercel.json now.json firebase.json app.json fly.toml railway.json
191
- serverless.yml Procfile Vagrantfile
192
- manifest.json robots.txt sitemap.xml humans.txt .htaccess favicon.ico
193
- index.html index.ts index.js main.ts main.go
194
- CLAUDE.md AGENTS.md
195
- `.trim().split(/\s+/);
196
-
197
- /** Folder names the theme gives a dedicated glyph. */
198
- const FOLDERS = `
199
- src app apps packages lib dist build public assets images fonts
200
- components hooks utils helpers shared config api server client services
201
- store views controllers routes middleware
202
- test tests __tests__ e2e coverage docs examples scripts bin
203
- database db migrations types locales i18n functions
204
- node_modules vendor .git .github .vscode .husky
205
- android ios logs tmp temp cache
206
- `.trim().split(/\s+/);
207
-
208
- /** `file_type_typescript.svg` → `file-type-typescript` (the Iconify key). */
209
- function iconifyKey(svgName: string | undefined): string | null {
210
- if (!svgName) return null;
211
- return svgName.replace(/\.svg$/, "").replace(/_/g, "-");
157
+ // ---------------------------------------------------------------------------
158
+ // The manifest
159
+ // ---------------------------------------------------------------------------
160
+
161
+ /** One entry of `supportedExtensions.ts` / `supportedFolders.ts`. */
162
+ interface ManifestEntry {
163
+ icon: string;
164
+ extensions: string[];
165
+ /** The strings in `extensions` are whole filenames, not suffixes. */
166
+ filename?: boolean;
167
+ /** Cartesian product with `extensionsGlob`, joined by a dot. */
168
+ filenamesGlob?: string[];
169
+ extensionsGlob?: string[];
170
+ languages?: { ids: string | string[]; knownExtensions?: string[]; knownFilenames?: string[] }[];
171
+ /** The theme ships a second drawing of this glyph, for a light workbench. */
172
+ light?: boolean;
173
+ /** Off unless a preset turns it on — `nest_*`, `ng_*`, the `*2` redraws. */
174
+ disabled?: boolean;
212
175
  }
213
176
 
214
- const collection = (await import("@iconify-json/vscode-icons/icons.json", {
215
- with: { type: "json" },
216
- })).default as { icons: Record<string, { body: string }>; width?: number; height?: number };
177
+ interface Manifest {
178
+ files: ManifestEntry[];
179
+ folders: ManifestEntry[];
180
+ }
181
+
182
+ /**
183
+ * Fetch the three manifest modules and evaluate them.
184
+ *
185
+ * They are TypeScript that imports its types from `../models`, so they are
186
+ * staged in one temp directory next to a stub supplying that module. Evaluating
187
+ * them is the point: these tables are 200 KB of nested literals, and a regex
188
+ * over them would be a second, worse parser that fails silently the first time
189
+ * upstream reformats a line.
190
+ */
191
+ async function loadManifest(): Promise<Manifest> {
192
+ const dir = mkdtempSync(resolve(tmpdir(), "ppm-vscode-icons-"));
193
+ try {
194
+ writeFileSync(
195
+ resolve(dir, "models.ts"),
196
+ "export const FileFormat = { svg: 'svg', png: 'png' };\n" +
197
+ "export const IFileCollection = undefined, IFolderCollection = undefined, ILanguage = undefined;\n",
198
+ );
199
+ for (const [name, digest] of Object.entries(MANIFEST_FILES)) {
200
+ const url =
201
+ `https://raw.githubusercontent.com/vscode-icons/vscode-icons/` +
202
+ `${MANIFEST_VERSION}/src/iconsManifest/${name}.ts`;
203
+ const res = await fetch(url);
204
+ if (!res.ok) throw new Error(`${url} → HTTP ${res.status}`);
205
+ const body = Buffer.from(await res.arrayBuffer());
206
+ const got = createHash("sha256").update(body).digest("hex");
207
+ if (got !== digest) {
208
+ throw new Error(`${name}.ts digest mismatch\n expected ${digest}\n got ${got}`);
209
+ }
210
+ writeFileSync(resolve(dir, `${name}.ts`), body.toString("utf8").replace("../models", "./models"));
211
+ }
212
+ const files = (await import(resolve(dir, "supportedExtensions.ts"))).extensions;
213
+ const folders = (await import(resolve(dir, "supportedFolders.ts"))).extensions;
214
+ return { files: files.supported, folders: folders.supported };
215
+ } finally {
216
+ rmSync(dir, { recursive: true, force: true });
217
+ }
218
+ }
219
+
220
+ /** `nest_controller_ts` → `file-type-nest-controller-ts`, the Iconify key. */
221
+ function iconifyKey(prefix: string, icon: string): string {
222
+ const key = `${prefix}-${icon.replace(/_/g, "-")}`.toLowerCase();
223
+ return ALIASES[key] ?? key;
224
+ }
225
+
226
+ /**
227
+ * The tables one preset of the manifest produces, built the way
228
+ * `manifestBuilder.buildFiles` builds them.
229
+ *
230
+ * Two details are load-bearing and neither is obvious. Entries are walked in
231
+ * order of *icon name*, because that is what upstream sorts by and later
232
+ * writes overwrite earlier ones — so two icons claiming `service.ts` resolve
233
+ * the same way here as they do in VS Code. And the language layer is written
234
+ * first and then overwritten by the explicit `extensions`, so a hand-declared
235
+ * association always beats one inferred from a language's known extensions.
236
+ */
237
+ function buildFiles(entries: ManifestEntry[]): {
238
+ fileNames: Record<string, string>;
239
+ fileExtensions: Record<string, string>;
240
+ langNames: Record<string, string>;
241
+ langExtensions: Record<string, string>;
242
+ } {
243
+ const langNames: Record<string, string> = {};
244
+ const langExtensions: Record<string, string> = {};
245
+ const names: Record<string, string> = {};
246
+ const extensions: Record<string, string> = {};
247
+
248
+ for (const entry of [...entries].sort((a, b) => (a.icon < b.icon ? -1 : a.icon > b.icon ? 1 : 0))) {
249
+ for (const lang of entry.languages ?? []) {
250
+ for (const ext of lang.knownExtensions ?? []) langExtensions[ext.toLowerCase()] = entry.icon;
251
+ for (const name of lang.knownFilenames ?? []) langNames[name.toLowerCase()] = entry.icon;
252
+ }
253
+ const put = (value: string) => {
254
+ if (entry.filename) names[value.toLowerCase()] = entry.icon;
255
+ // `removeFirstDot`: the manifest writes `.babelrc` and means the suffix
256
+ // `babelrc`. Only the *leading* dot goes — `controller.ts` stays whole.
257
+ else extensions[value.replace(/^\./, "").toLowerCase()] = entry.icon;
258
+ };
259
+ for (const value of entry.extensions) put(value);
260
+ if (entry.filenamesGlob?.length && entry.extensionsGlob?.length) {
261
+ for (const stem of entry.filenamesGlob) for (const ext of entry.extensionsGlob) put(`${stem}.${ext}`);
262
+ }
263
+ }
264
+ // The language layer is handed back *separately* rather than merged under the
265
+ // explicit one, because it is the weaker claim in a way the merge cannot
266
+ // express. `knownExtensions` flattens "this language is known by .css" into
267
+ // "a .css file is this icon", and two languages may claim one extension —
268
+ // `tailwindcss` also lists `css`, sorts after it, and so every stylesheet in
269
+ // the app came out with the Tailwind logo. VS Code never sees that collision
270
+ // because it matches the file's *actual* language id. So this layer ranks
271
+ // below `vscode-icons-js`'s flattening of VS Code's own associations, and
272
+ // only answers for languages that package is too old to know.
273
+ return { fileNames: names, fileExtensions: extensions, langNames, langExtensions };
274
+ }
275
+
276
+ // ---------------------------------------------------------------------------
277
+ // The artwork
278
+ // ---------------------------------------------------------------------------
279
+
280
+ const collection = (
281
+ await import("@iconify-json/vscode-icons/icons.json", { with: { type: "json" } })
282
+ ).default as { icons: Record<string, { body: string }>; width?: number; height?: number };
217
283
 
218
284
  /**
219
285
  * The *artwork* version, which is the one the notice has to name.
@@ -234,39 +300,23 @@ const mappingVersion = (
234
300
  /** Both drawings of one glyph; `light` only when the theme ships a second one. */
235
301
  type Glyph = { dark: string; light?: string };
236
302
 
237
- const used = new Map<string, Glyph>(); // iconify key → bodies
303
+ const used = new Map<string, Glyph>();
238
304
  const missing = new Set<string>();
239
305
  const overBudget = new Map<string, number>();
240
306
  let lightVariants = 0;
241
307
 
242
308
  /**
243
- * `file_type_light_json` `file_type_json`.
309
+ * Claim a glyph, or report why it cannot be drawn.
244
310
  *
245
- * The mapping tables answer with the **light** name for 143 of these glyphs and
246
- * there is no option to ask for the other one`getIconForFile("a.json")` is
247
- * `file_type_light_json` full stop. Those are the drawings vscode-icons uses
248
- * when the *workbench* theme is light: `#fbc02d` where the normal one is
249
- * `#f5de19`, and for `toml` a path with no `fill` at all, i.e. black. Shipping
250
- * them as the only artwork is how the JSON braces came out muddy and the TOML
251
- * glyph came out invisible on every dark PPM theme. So the class is named after
252
- * the theme-independent glyph and carries both drawings.
311
+ * A `null` here is not an error at the call site: the name simply gets no entry
312
+ * in the tables, and the runtime falls through to a shorter suffix which for
313
+ * `foo.controller.ts` means the TypeScript glyph rather than a blank page.
253
314
  */
254
- function canonical(key: string): string {
255
- return key.replace(/^file-type-light-/, "file-type-");
256
- }
257
-
258
- function take(svgName: string | undefined): string | null {
259
- const named = iconifyKey(svgName);
260
- return named === null ? null : takeKey(named);
261
- }
262
-
263
- function takeKey(named: string): string | null {
264
- const aliased = ALIASES[named] ?? named;
265
- const key = canonical(aliased);
315
+ function takeIcon(key: string): string | null {
266
316
  if (used.has(key)) return key;
267
317
  const icon = collection.icons[key];
268
318
  if (!icon) {
269
- // A name the mapping knows and the collection does not: the two are
319
+ // A name the manifest knows and the collection does not: the two are
270
320
  // versioned separately, so this is reported rather than silently dropped.
271
321
  missing.add(key);
272
322
  return null;
@@ -275,56 +325,208 @@ function takeKey(named: string): string | null {
275
325
  overBudget.set(key, icon.body.length);
276
326
  return null;
277
327
  }
278
- const light = collection.icons[key.replace(/^file-type-/, "file-type-light-")];
328
+ // The theme draws 132 of these twice, the second for a light workbench:
329
+ // `#fbc02d` where the normal one is `#f5de19`, and for `toml` a path with no
330
+ // `fill` at all, i.e. black. Shipping the light one as the *only* artwork is
331
+ // how the TOML glyph came out invisible on every dark PPM theme, so the class
332
+ // is named after the theme-independent glyph and carries both drawings.
333
+ // One `replace` per prefix, never a `??` chain: a pattern that does not match
334
+ // returns the key *unchanged*, so chaining would find the dark drawing again
335
+ // and ship it as its own light variant — 1187 of them, silently.
336
+ const lightKey = key.startsWith("file-type-")
337
+ ? key.replace(/^file-type-/, "file-type-light-")
338
+ : key.startsWith("folder-type-")
339
+ ? key.replace(/^folder-type-/, "folder-type-light-")
340
+ : null;
341
+ const light = lightKey === null ? undefined : collection.icons[lightKey];
279
342
  // A light drawing over the budget is simply left out: the class still has its
280
343
  // dark one, which is legible on a light background, just not tuned for it.
281
- const within = light && light.body.length <= MAX_BODY_BYTES;
344
+ const within = light !== undefined && light.body.length <= MAX_BODY_BYTES;
282
345
  if (within) lightVariants++;
283
- used.set(key, { dark: icon.body, light: within ? light.body : undefined });
346
+ used.set(key, { dark: icon.body, light: within ? light!.body : undefined });
284
347
  return key;
285
348
  }
286
349
 
287
- const DEFAULT_FILE = take("default_file.svg")!;
288
- const DEFAULT_FOLDER = take("default_folder.svg")!;
289
- const DEFAULT_FOLDER_OPEN = take("default_folder_opened.svg")!;
350
+ /**
351
+ * Rewrite tables of manifest icon names into one table of drawable classes,
352
+ * earlier tables winning.
353
+ *
354
+ * Per key rather than merging first and resolving after, and that is the whole
355
+ * point: a key the manifest claims with a glyph that is missing or over the
356
+ * budget would otherwise be *lost* instead of falling through — `cargo.lock`
357
+ * reached `file-type-cargo`, which the collection does not have, and ended up
358
+ * with no icon at all while the second source's answer sat right there.
359
+ */
360
+ function resolve_(prefix: string, ...tables: Record<string, string>[]): Record<string, string> {
361
+ const out: Record<string, string> = {};
362
+ for (const name of new Set(tables.flatMap((t) => Object.keys(t)))) {
363
+ for (const table of tables) {
364
+ const icon = table[name];
365
+ if (icon === undefined) continue;
366
+ const key = takeIcon(iconifyKey(prefix, icon));
367
+ if (key) {
368
+ out[name] = key;
369
+ break;
370
+ }
371
+ }
372
+ }
373
+ return out;
374
+ }
375
+
376
+ // ---------------------------------------------------------------------------
377
+ // Build
378
+ // ---------------------------------------------------------------------------
379
+
380
+ const manifest = await loadManifest();
381
+
382
+ const DEFAULT_FILE = takeIcon("default-file")!;
383
+ const DEFAULT_FOLDER = takeIcon("default-folder")!;
384
+ const DEFAULT_FOLDER_OPEN = takeIcon("default-folder-opened")!;
385
+
386
+ /**
387
+ * The presets, which are the reason this is three passes rather than one.
388
+ *
389
+ * `nest_*` and `ng_*` ship `disabled: true` — in VS Code they are behind
390
+ * `vsicons.presets.nestjs` and `vsicons.presets.angular`, off by default and
391
+ * *mutually exclusive in practice*: they claim the same six suffixes
392
+ * (`module`, `service`, `guard`, `pipe`, `interceptor`, `controller`, each in
393
+ * `.ts` and `.js`), and enabling both would silently hand a NestJS repository
394
+ * the Angular artwork, since `ng_` sorts after `nest_`.
395
+ *
396
+ * So the base tables have neither, and each preset is emitted as an overlay the
397
+ * runtime consults first once it knows which framework the project is built
398
+ * with. Off both overlays, `app.service.ts` falls through to `.ts` — the plain
399
+ * TypeScript glyph, which is what VS Code with no preset also shows.
400
+ */
401
+ const isNest = (icon: string) => icon.startsWith("nest_");
402
+ const isAngular = (icon: string) => icon.startsWith("ng_");
403
+
404
+ const base = buildFiles(manifest.files.filter((e) => e.icon && !e.disabled));
405
+ const withNest = buildFiles(manifest.files.filter((e) => e.icon && (!e.disabled || isNest(e.icon))));
406
+ const withAngular = buildFiles(
407
+ manifest.files.filter((e) => e.icon && (!e.disabled || isAngular(e.icon))),
408
+ );
290
409
 
291
- /** An override if there is one, else whatever the mapping tables answer. */
292
- function iconFor(name: string, mapped: string | undefined): string | null {
293
- const override = OVERRIDES[name];
294
- return override ? takeKey(override) : take(mapped);
410
+ /**
411
+ * The second source, consulted only where the manifest said nothing.
412
+ *
413
+ * `file_type_typescript.svg` `typescript`, back into the manifest's own
414
+ * vocabulary so both layers go through one `resolve_` and one budget check.
415
+ */
416
+ async function legacyTable(name: string): Promise<Record<string, string>> {
417
+ const table = (await import(`vscode-icons-js/dist/generated/${name}`))[name] as Record<
418
+ string,
419
+ string
420
+ >;
421
+ const out: Record<string, string> = {};
422
+ for (const [key, svg] of Object.entries(table)) {
423
+ // `file_type_light_ini.svg` → `ini`. These tables answer with the **light**
424
+ // drawing for 143 glyphs and offer no way to ask for the other one, and
425
+ // those are darker ink meant for a light workbench — `light_toml` has no
426
+ // `fill` at all, i.e. black on black. The class is named after the
427
+ // theme-independent glyph and `takeIcon` attaches both drawings.
428
+ out[key.toLowerCase()] = svg
429
+ .replace(/\.svg$/, "")
430
+ .replace(/^(file|folder)_type_/, "")
431
+ .replace(/^light_/, "");
432
+ }
433
+ return out;
295
434
  }
296
435
 
297
- const extIcon: Record<string, string> = {};
298
- for (const ext of [...Object.keys(FileExtensions2ToIcon), ...EXTENSIONS]) {
299
- const key = iconFor(ext, getIconForFile(`file.${ext}`));
300
- // Only worth an entry when it differs from the fallback the component uses.
301
- if (key && key !== DEFAULT_FILE) extIcon[ext] = key;
436
+ /** `b` fills the keys `a` has no answer for; `a` always wins. */
437
+ function fill(a: Record<string, string>, b: Record<string, string>): Record<string, string> {
438
+ return { ...b, ...a };
302
439
  }
303
440
 
304
- const nameIcon: Record<string, string> = {};
305
- for (const name of FILENAMES) {
306
- const key = iconFor(name, getIconForFile(name));
307
- if (!key || key === DEFAULT_FILE) continue;
308
- // Skip a filename whose icon its own extension already gives: `main.go` and
309
- // `.go` resolve to the same glyph, and the extension table already covers it.
310
- const ext = name.includes(".") ? name.split(".").pop()!.toLowerCase() : "";
311
- if (ext && extIcon[ext] === key) continue;
312
- nameIcon[name.toLowerCase()] = key;
441
+ const legacyExtensions = fill(
442
+ await legacyTable("FileExtensions2ToIcon"),
443
+ await legacyTable("FileExtensions1ToIcon"),
444
+ );
445
+ /**
446
+ * The table that actually earns `vscode-icons-js` its place.
447
+ *
448
+ * It is that package's flattening of VS Code's *language* associations — `cc`
449
+ * and `cxx` to C++, `htm` to HTML, `conf` to INI — which is precisely what the
450
+ * icon manifest leaves to the editor and a port therefore has to find
451
+ * somewhere.
452
+ */
453
+ const legacyLanguages = await legacyTable("LanguagesToIcon");
454
+ const legacyNames = await legacyTable("FileNamesToIcon");
455
+ const legacyFolders = await legacyTable("FolderNamesToIcon");
456
+
457
+ const extensionSources = [
458
+ base.fileExtensions,
459
+ legacyExtensions,
460
+ legacyLanguages,
461
+ base.langExtensions,
462
+ LANGUAGE_EXTRAS.extensions,
463
+ ];
464
+ const nameSources = [base.fileNames, legacyNames, base.langNames, LANGUAGE_EXTRAS.names];
465
+
466
+ const extensionIcons = resolve_("file-type", ...extensionSources);
467
+ const filenameIcons = resolve_("file-type", ...nameSources);
468
+
469
+ /**
470
+ * The same cascade, un-resolved, for the preset diff below.
471
+ *
472
+ * The overlay has to be compared at *manifest icon name* level — against the
473
+ * already-resolved class names it would differ on every single key, because
474
+ * `nest_service_ts` is never equal to `file-type-typescript`, and the two
475
+ * overlays came out at 2459 entries instead of 22.
476
+ */
477
+ const baseNameSource = nameSources.reduceRight((acc, t) => fill(t, acc));
478
+ const baseExtensionSource = extensionSources.reduceRight((acc, t) => fill(t, acc));
479
+
480
+ /**
481
+ * Only what the preset *changes*, so the overlay is 45 lines rather than 1000.
482
+ *
483
+ * Diffed against the *combined* base rather than the manifest's, or an entry
484
+ * the second source already answers identically would ship twice.
485
+ */
486
+ function overlay(
487
+ preset: { fileNames: Record<string, string>; fileExtensions: Record<string, string> },
488
+ ): { names: Record<string, string>; extensions: Record<string, string> } {
489
+ const diff = (a: Record<string, string>, b: Record<string, string>) =>
490
+ Object.fromEntries(Object.entries(a).filter(([k, v]) => b[k] !== v));
491
+ return {
492
+ names: resolve_("file-type", diff(preset.fileNames, baseNameSource)),
493
+ extensions: resolve_("file-type", diff(preset.fileExtensions, baseExtensionSource)),
494
+ };
495
+ }
496
+
497
+ const frameworks = { nest: overlay(withNest), angular: overlay(withAngular) };
498
+
499
+ /** `{ … }` on one line when there is nothing in it, so the output reads clean. */
500
+ function block(map: Record<string, string>, key: string): string {
501
+ return Object.keys(map).length === 0 ? ` ${key}: {},` : ` ${key}: {\n${record(map, " ")}\n },`;
502
+ }
503
+
504
+ const folderTables = buildFiles(manifest.folders.filter((e) => e.icon && !e.disabled));
505
+ const baseFolders = fill(folderTables.fileExtensions, legacyFolders);
506
+ const folderIcons = resolve_("folder-type", folderTables.fileExtensions, legacyFolders);
507
+ const folderOpenIcons: Record<string, string> = {};
508
+ for (const [name, icon] of Object.entries(baseFolders)) {
509
+ const key = takeIcon(iconifyKey("folder-type", `${icon}_opened`));
510
+ if (key) folderOpenIcons[name] = key;
313
511
  }
314
512
 
315
- const folderIcon: Record<string, string> = {};
316
- const folderOpenIcon: Record<string, string> = {};
317
- for (const folder of FOLDERS) {
318
- const closed = take(getIconForFolder(folder));
319
- const open = take(getIconForOpenFolder(folder));
320
- if (closed && closed !== DEFAULT_FOLDER) folderIcon[folder.toLowerCase()] = closed;
321
- if (open && open !== DEFAULT_FOLDER_OPEN) folderOpenIcon[folder.toLowerCase()] = open;
513
+ // A folder whose closed glyph was dropped but whose open one survived would
514
+ // change picture on expand for no reason; keep the pair or neither.
515
+ for (const name of Object.keys(folderOpenIcons)) {
516
+ if (!folderIcons[name]) delete folderOpenIcons[name];
322
517
  }
518
+ for (const name of Object.keys(folderIcons)) {
519
+ if (!folderOpenIcons[name]) delete folderIcons[name];
520
+ }
521
+
522
+ // ---------------------------------------------------------------------------
523
+ // Emit
524
+ // ---------------------------------------------------------------------------
323
525
 
324
- function record(map: Record<string, string>): string {
526
+ function record(map: Record<string, string>, indent = " "): string {
325
527
  return Object.keys(map)
326
528
  .sort()
327
- .map((k) => ` ${JSON.stringify(k)}: ${JSON.stringify(map[k])},`)
529
+ .map((k) => `${indent}${JSON.stringify(k)}: ${JSON.stringify(map[k])},`)
328
530
  .join("\n");
329
531
  }
330
532
 
@@ -334,9 +536,10 @@ const names = [...used.keys()].sort();
334
536
  const ts = `/**
335
537
  * GENERATED by \`bun scripts/gen-file-icons.ts\` — do not edit.
336
538
  *
337
- * Which glyph of the vscode-icons theme (MIT, vscode-icons-team) a name gets.
539
+ * Which glyph of the vscode-icons theme (MIT, vscode-icons-team) a name gets,
540
+ * built from that extension's own \`src/iconsManifest\` at ${MANIFEST_VERSION}.
338
541
  * The glyphs themselves are in \`src/web/styles/file-icons.generated.css\`, one
339
- * class each: 500 KB of full-colour SVG has no business in a JS bundle, and as
542
+ * class each: 3 MB of full-colour SVG has no business in a JS bundle, and as
340
543
  * CSS the browser decodes each icon once however many rows use it — which is
341
544
  * also how VS Code draws its own file icon themes.
342
545
  *
@@ -349,37 +552,78 @@ const ts = `/**
349
552
  * be called \`constructor\`; a directory really can be called \`toString\`.
350
553
  */
351
554
 
352
- /** Lowercased extension → icon class suffix. */
555
+ /** Lowercased extension → icon class suffix. Longest suffix wins. */
353
556
  export const EXTENSION_ICONS: Record<string, string> = {
354
- ${record(extIcon)}
557
+ ${record(extensionIcons)}
355
558
  };
356
559
 
357
- /** Lowercased whole filename → icon; checked before the extension. */
560
+ /** Lowercased whole filename → icon; checked before any extension. */
358
561
  export const FILENAME_ICONS: Record<string, string> = {
359
- ${record(nameIcon)}
562
+ ${record(filenameIcons)}
563
+ };
564
+
565
+ /**
566
+ * The frameworks whose file-naming convention has artwork of its own.
567
+ *
568
+ * Upstream ships these as presets a user turns on, because they collide: both
569
+ * claim \`.module.ts\`, \`.service.ts\`, \`.guard.ts\`, \`.pipe.ts\`,
570
+ * \`.interceptor.ts\` and \`.controller.ts\`. PPM picks one per project instead —
571
+ * see \`project-framework-store.ts\`.
572
+ */
573
+ export type IconFramework = ${Object.keys(frameworks)
574
+ .map((k) => JSON.stringify(k))
575
+ .join(" | ")};
576
+
577
+ /** Checked before {@link EXTENSION_ICONS} when a project names a framework. */
578
+ export const FRAMEWORK_EXTENSION_ICONS: Record<IconFramework, Record<string, string>> = {
579
+ ${Object.entries(frameworks)
580
+ .map(([key, o]) => block(o.extensions, key))
581
+ .join("\n")}
582
+ };
583
+
584
+ /** Checked before {@link FILENAME_ICONS} when a project names a framework. */
585
+ export const FRAMEWORK_FILENAME_ICONS: Record<IconFramework, Record<string, string>> = {
586
+ ${Object.entries(frameworks)
587
+ .map(([key, o]) => block(o.names, key))
588
+ .join("\n")}
360
589
  };
361
590
 
362
591
  /** Lowercased folder name → icon. */
363
592
  export const FOLDER_ICONS: Record<string, string> = {
364
- ${record(folderIcon)}
593
+ ${record(folderIcons)}
365
594
  };
366
595
 
367
596
  /** Lowercased folder name → icon, for an expanded folder. */
368
597
  export const FOLDER_OPEN_ICONS: Record<string, string> = {
369
- ${record(folderOpenIcon)}
598
+ ${record(folderOpenIcons)}
370
599
  };
371
600
 
372
601
  export const DEFAULT_FILE_ICON = ${JSON.stringify(DEFAULT_FILE)};
373
602
  export const DEFAULT_FOLDER_ICON = ${JSON.stringify(DEFAULT_FOLDER)};
374
603
  export const DEFAULT_FOLDER_OPEN_ICON = ${JSON.stringify(DEFAULT_FOLDER_OPEN)};
375
604
 
605
+ /** The longest suffix any entry of {@link EXTENSION_ICONS} is, in dot-segments. */
606
+ export const MAX_EXTENSION_SEGMENTS = ${Math.max(
607
+ ...Object.keys(extensionIcons).map((k) => k.split(".").length),
608
+ ...Object.values(frameworks).flatMap((f) => Object.keys(f.extensions).map((k) => k.split(".").length)),
609
+ )};
610
+
376
611
  /** Every icon this module can name, for the test that pairs the two files. */
377
612
  export const ICON_NAMES: readonly string[] = ${JSON.stringify(names)};
378
613
 
379
614
  // After the literals rather than \`Object.create(null)\` around each, so the
380
615
  // tables above stay readable as data and \`Record<string, string>\` still
381
- // describes them.
382
- for (const table of [EXTENSION_ICONS, FILENAME_ICONS, FOLDER_ICONS, FOLDER_OPEN_ICONS]) {
616
+ // describes them. The framework overlays are included because they are indexed
617
+ // by filename too \`fileIconName\` consults them *before* the plain tables, so
618
+ // an inherited property there answers first.
619
+ for (const table of [
620
+ EXTENSION_ICONS,
621
+ FILENAME_ICONS,
622
+ FOLDER_ICONS,
623
+ FOLDER_OPEN_ICONS,
624
+ ...Object.values(FRAMEWORK_EXTENSION_ICONS),
625
+ ...Object.values(FRAMEWORK_FILENAME_ICONS),
626
+ ]) {
383
627
  Object.setPrototypeOf(table, null);
384
628
  }
385
629
  `;
@@ -466,9 +710,16 @@ const bodyBytes = [...used.values()].reduce(
466
710
  0,
467
711
  );
468
712
  console.log(
469
- `file icons ${used.size} glyphs (${lightVariants} with a light-theme drawing) ` +
470
- `${Object.keys(extIcon).length} extensions ` +
471
- `${Object.keys(nameIcon).length} filenames ${Object.keys(folderIcon).length} folders`,
713
+ `file icons vscode-icons ${MANIFEST_VERSION} ${used.size} glyphs ` +
714
+ `(${lightVariants} with a light-theme drawing)`,
715
+ );
716
+ console.log(
717
+ ` ${Object.keys(extensionIcons).length} extensions ` +
718
+ `${Object.keys(filenameIcons).length} filenames ` +
719
+ `${Object.keys(folderIcons).length} folders ` +
720
+ Object.entries(frameworks)
721
+ .map(([k, o]) => `${Object.keys(o.extensions).length + Object.keys(o.names).length} ${k}`)
722
+ .join(" "),
472
723
  );
473
724
  console.log(
474
725
  ` ${(Buffer.byteLength(ts) / 1024).toFixed(1)} KiB of mapping (JS) ` +