shadow-claw 1.40.1 → 1.41.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (325) hide show
  1. package/.well-known/mcp/server-card.json +1 -1
  2. package/.well-known/mcp.json +1 -1
  3. package/README.md +2 -1
  4. package/dist/cli/build/build.js +1 -1
  5. package/dist/cli/{chunk-Bfz2NYgD.js → chunk-0IeC97mU.js} +7 -7
  6. package/dist/cli/chunk-3oLxlxPn.js +1 -0
  7. package/dist/cli/chunk-63s8NnIS.js +93 -0
  8. package/dist/cli/{chunk-BXg4-9Rq2.js → chunk-8CrZyP1b2.js} +1 -1
  9. package/dist/cli/{chunk-BP5Hg6CM.js → chunk-B--ueArB.js} +1 -1
  10. package/dist/cli/chunk-BATVgkFY.js +17 -0
  11. package/dist/cli/chunk-BIh7rXlJ2.js +1 -0
  12. package/dist/cli/{chunk-Drr0VSGA2.js → chunk-BKnTOLLS2.js} +1 -1
  13. package/dist/cli/{chunk-C5PuRV08.js → chunk-BLMC1Bxv.js} +1 -1
  14. package/dist/cli/{chunk-B2tNSlFy2.js → chunk-BOigzz2U2.js} +1 -1
  15. package/dist/cli/{chunk-DOf3DZdR.js → chunk-BOzn335y.js} +1 -1
  16. package/dist/cli/chunk-BjOeVLb42.js +1 -0
  17. package/dist/cli/{chunk-DqTx0rRp2.js → chunk-Bjqh5_SU2.js} +1 -1
  18. package/dist/cli/chunk-BqWBd_jF2.js +1 -0
  19. package/dist/cli/{chunk-DIbSoQmd2.js → chunk-C6FFByBU2.js} +2 -2
  20. package/dist/cli/{chunk-Drq18VZe.js → chunk-C9-aL3rF.js} +1 -1
  21. package/dist/cli/{chunk-BUhqHeEA2.js → chunk-C9czddEh2.js} +2 -2
  22. package/dist/cli/chunk-CCx8WOwz2.js +1 -0
  23. package/dist/cli/{chunk-BkMSjVJb.js → chunk-CK6ejMig.js} +1 -1
  24. package/dist/cli/chunk-CUUERioR2.js +1 -0
  25. package/dist/cli/chunk-CXAobnJd.js +1 -0
  26. package/dist/cli/chunk-CY1X0pOF2.js +1 -0
  27. package/dist/cli/chunk-CZvvgLVv.js +1 -0
  28. package/dist/cli/chunk-CbcwbfJB.js +1 -0
  29. package/dist/cli/{chunk-quilB3TV2.js → chunk-Ci9OJGcd2.js} +1 -1
  30. package/dist/cli/chunk-Cj9wl0qx.js +1 -0
  31. package/dist/cli/{chunk-3gRhOmGo2.js → chunk-CjJsVrKI2.js} +1 -1
  32. package/dist/cli/{chunk-CmEfzm-_.js → chunk-ClPZ4Rea.js} +1 -1
  33. package/dist/cli/chunk-Cmdxoq9q.js +13 -0
  34. package/dist/cli/{chunk-WMPAfIvV2.js → chunk-DHJCGRuG2.js} +1 -1
  35. package/dist/cli/{chunk-C7Y2xIk4.js → chunk-DKVkYTV9.js} +1 -1
  36. package/dist/cli/chunk-DR2XtWRo.js +1 -0
  37. package/dist/cli/{chunk-BiVJVh_S.js → chunk-DUlIqkaX.js} +7 -7
  38. package/dist/cli/{chunk-CkpueGaZ2.js → chunk-DjWle8FF2.js} +1 -1
  39. package/dist/cli/{chunk-CSvHmNOk.js → chunk-DmRXVLfy.js} +1 -1
  40. package/dist/cli/{chunk-BT1xXZn72.js → chunk-HlSiTC-u2.js} +1 -1
  41. package/dist/cli/{chunk-da0bsiOn.js → chunk-P4JSeFBI.js} +1 -1
  42. package/dist/cli/chunk-UcNbHaRQ.js +1 -0
  43. package/dist/cli/{chunk-BXFHFbEd.js → chunk-UweLmfQS.js} +1 -1
  44. package/dist/cli/{chunk-CGiXWHTi2.js → chunk-gwpxl-0W2.js} +1 -1
  45. package/dist/cli/{chunk-NH8koEis2.js → chunk-nmDhxXH_2.js} +1 -1
  46. package/dist/cli/chunk-p0n_c-I2.js +1 -0
  47. package/dist/cli/{chunk-D7xP72La.js → chunk-q7r4FosZ.js} +1 -1
  48. package/dist/cli/cli.js +1 -1
  49. package/dist/cli/prerender/dsd-shell/prerender-dsd-shell.js +1 -1
  50. package/dist/electron/main.cjs +1 -1
  51. package/dist/headless-agent.js +1 -1
  52. package/dist/lib/{app-routes-BAEMLu9g.js → app-routes-CKkiX12g.js} +7 -2
  53. package/dist/lib/app-routes-CKkiX12g.js.map +1 -0
  54. package/dist/lib/components/index.js +26 -26
  55. package/dist/lib/components/settings/shadow-claw-accounts.js +1 -1
  56. package/dist/lib/components/settings/shadow-claw-channel-config.js +1 -1
  57. package/dist/lib/components/settings/shadow-claw-control-plane.js +1 -1
  58. package/dist/lib/components/settings/shadow-claw-git.js +1 -1
  59. package/dist/lib/components/settings/shadow-claw-integrations.js +1 -1
  60. package/dist/lib/components/settings/shadow-claw-llm.js +1 -1
  61. package/dist/lib/components/settings/shadow-claw-mcp-remote.js +1 -1
  62. package/dist/lib/components/settings/shadow-claw-networking.js +1 -1
  63. package/dist/lib/components/settings/shadow-claw-notifications.js +1 -1
  64. package/dist/lib/components/settings/shadow-claw-peerjs.js +1 -1
  65. package/dist/lib/components/settings/shadow-claw-storage.js +1 -1
  66. package/dist/lib/components/settings/shadow-claw-task-server.js +1 -1
  67. package/dist/lib/components/settings/shadow-claw-webvm.js +1 -1
  68. package/dist/lib/components/shadow-claw/utils/syncSlottedSidebarActiveLinks.d.ts +10 -0
  69. package/dist/lib/components/shadow-claw-a2ui.js +1 -1
  70. package/dist/lib/components/shadow-claw-accounts.js +1 -1
  71. package/dist/lib/components/shadow-claw-channel-config.js +1 -1
  72. package/dist/lib/components/shadow-claw-channels.js +1 -1
  73. package/dist/lib/components/shadow-claw-chat.js +1 -1
  74. package/dist/lib/components/shadow-claw-control-plane.js +1 -1
  75. package/dist/lib/components/shadow-claw-conversations.js +1 -1
  76. package/dist/lib/components/shadow-claw-file-viewer.js +1 -1
  77. package/dist/lib/components/shadow-claw-files.js +1 -1
  78. package/dist/lib/components/shadow-claw-git.js +1 -1
  79. package/dist/lib/components/shadow-claw-integrations.js +1 -1
  80. package/dist/lib/components/shadow-claw-llm.js +1 -1
  81. package/dist/lib/components/shadow-claw-mcp-remote.js +1 -1
  82. package/dist/lib/components/shadow-claw-networking.js +1 -1
  83. package/dist/lib/components/shadow-claw-notifications.js +1 -1
  84. package/dist/lib/components/shadow-claw-page-header.js +1 -1
  85. package/dist/lib/components/shadow-claw-pages.js +1 -1
  86. package/dist/lib/components/shadow-claw-peerjs.js +1 -1
  87. package/dist/lib/components/shadow-claw-settings.js +1 -1
  88. package/dist/lib/components/shadow-claw-storage.js +1 -1
  89. package/dist/lib/components/shadow-claw-task-server.js +1 -1
  90. package/dist/lib/components/shadow-claw-tasks.js +1 -1
  91. package/dist/lib/components/shadow-claw-terminal.js +1 -1
  92. package/dist/lib/components/shadow-claw-tools.js +1 -1
  93. package/dist/lib/components/shadow-claw-webvm.js +1 -1
  94. package/dist/lib/components/shadow-claw.js +1 -1
  95. package/dist/lib/{config-CPHDmyTx.js → config-DZkF9LiO.js} +2 -2
  96. package/dist/lib/{config-CPHDmyTx.js.map → config-DZkF9LiO.js.map} +1 -1
  97. package/dist/lib/{configurePeerJs-BsJoycQd.js → configurePeerJs-B3IE9BY2.js} +3 -3
  98. package/dist/lib/{configurePeerJs-BsJoycQd.js.map → configurePeerJs-B3IE9BY2.js.map} +1 -1
  99. package/dist/lib/{connections-BdIru7bF.js → connections-CalJGWvZ.js} +2 -2
  100. package/dist/lib/{connections-BdIru7bF.js.map → connections-CalJGWvZ.js.map} +1 -1
  101. package/dist/lib/{downloadGroupFile-Dn0KsMlV.js → downloadGroupFile-DDE1fIhJ.js} +2 -2
  102. package/dist/lib/{downloadGroupFile-Dn0KsMlV.js.map → downloadGroupFile-DDE1fIhJ.js.map} +1 -1
  103. package/dist/lib/{file-viewer-BYrXXu-X.js → file-viewer-Bll2EoGc.js} +5 -5
  104. package/dist/lib/{file-viewer-BYrXXu-X.js.map → file-viewer-Bll2EoGc.js.map} +1 -1
  105. package/dist/lib/{getGroupDir-DF-RZO8X.js → getGroupDir-MHv7Lvmp.js} +4 -4
  106. package/dist/lib/{getGroupDir-DF-RZO8X.js.map → getGroupDir-MHv7Lvmp.js.map} +1 -1
  107. package/dist/lib/{git-s_0cgKm1.js → git-Bv_M3dgw.js} +3 -3
  108. package/dist/lib/{git-s_0cgKm1.js.map → git-Bv_M3dgw.js.map} +1 -1
  109. package/dist/lib/{iframe-sanitizer-DFdda1jJ.js → iframe-sanitizer-C7ZGDjEI.js} +2 -2
  110. package/dist/lib/{iframe-sanitizer-DFdda1jJ.js.map → iframe-sanitizer-C7ZGDjEI.js.map} +1 -1
  111. package/dist/lib/{iframe-storage-proxy-qrhZmQyO.js → iframe-storage-proxy-CR8ZYHPd.js} +2 -2
  112. package/dist/lib/{iframe-storage-proxy-qrhZmQyO.js.map → iframe-storage-proxy-CR8ZYHPd.js.map} +1 -1
  113. package/dist/lib/index.js +27 -27
  114. package/dist/lib/{initChatSplitResize-DuNlwCuI.js → initChatSplitResize-DiKTI-wk.js} +2 -2
  115. package/dist/lib/{initChatSplitResize-DuNlwCuI.js.map → initChatSplitResize-DiKTI-wk.js.map} +1 -1
  116. package/dist/lib/{initControlPlane-D4UZ4EhV.js → initControlPlane-CzjtQ4aM.js} +15 -15
  117. package/dist/lib/{initControlPlane-D4UZ4EhV.js.map → initControlPlane-CzjtQ4aM.js.map} +1 -1
  118. package/dist/lib/{markdown-CRRwbN9h.js → markdown-k8wA9LKy.js} +4 -4
  119. package/dist/lib/{markdown-CRRwbN9h.js.map → markdown-k8wA9LKy.js.map} +1 -1
  120. package/dist/lib/{mcp-reconnect-COdZ-cea.js → mcp-reconnect-CGIdfZnS.js} +2 -2
  121. package/dist/lib/{mcp-reconnect-COdZ-cea.js.map → mcp-reconnect-CGIdfZnS.js.map} +1 -1
  122. package/dist/lib/{memoryStorage-BsMe2FOQ.js → memoryStorage-DL-Bo3dh.js} +2 -2
  123. package/dist/lib/{memoryStorage-BsMe2FOQ.js.map → memoryStorage-DL-Bo3dh.js.map} +1 -1
  124. package/dist/lib/{orchestrator-D9f5fDCb.js → orchestrator-DKHCoikY.js} +11 -11
  125. package/dist/lib/{orchestrator-D9f5fDCb.js.map → orchestrator-DKHCoikY.js.map} +1 -1
  126. package/dist/lib/{orchestrator-CMN43L0F.js → orchestrator-Dn1ZOz9B.js} +12 -12
  127. package/dist/lib/{orchestrator-CMN43L0F.js.map → orchestrator-Dn1ZOz9B.js.map} +1 -1
  128. package/dist/lib/{peerjs-6u8KtenE.js → peerjs-BK41iBvP.js} +7 -7
  129. package/dist/lib/{peerjs-6u8KtenE.js.map → peerjs-BK41iBvP.js.map} +1 -1
  130. package/dist/lib/{push-client-DCnB2-OU.js → push-client-DKgVFD2Z.js} +3 -3
  131. package/dist/lib/{push-client-DCnB2-OU.js.map → push-client-DKgVFD2Z.js.map} +1 -1
  132. package/dist/lib/{readGroupFileBytes-nKNJ6sbl.js → readGroupFileBytes-CgU_UfGv.js} +2 -2
  133. package/dist/lib/{readGroupFileBytes-nKNJ6sbl.js.map → readGroupFileBytes-CgU_UfGv.js.map} +1 -1
  134. package/dist/lib/{shadow-claw-4x3LqlII.js → shadow-claw-CtCj5Rn_.js} +121 -16
  135. package/dist/lib/shadow-claw-CtCj5Rn_.js.map +1 -0
  136. package/dist/lib/{shadow-claw-a2ui-Dipe8nY2.js → shadow-claw-a2ui-CvK5w940.js} +2 -2
  137. package/dist/lib/{shadow-claw-a2ui-Dipe8nY2.js.map → shadow-claw-a2ui-CvK5w940.js.map} +1 -1
  138. package/dist/lib/{shadow-claw-accounts-BgCkz3Zv.js → shadow-claw-accounts-Br3XdHro.js} +3 -3
  139. package/dist/lib/{shadow-claw-accounts-BgCkz3Zv.js.map → shadow-claw-accounts-Br3XdHro.js.map} +1 -1
  140. package/dist/lib/{shadow-claw-channel-config-CIm_Bxd2.js → shadow-claw-channel-config-Da2dN19M.js} +3 -3
  141. package/dist/lib/{shadow-claw-channel-config-CIm_Bxd2.js.map → shadow-claw-channel-config-Da2dN19M.js.map} +1 -1
  142. package/dist/lib/{shadow-claw-channels-CaceK52s.js → shadow-claw-channels-BofYHgz8.js} +3 -3
  143. package/dist/lib/{shadow-claw-channels-CaceK52s.js.map → shadow-claw-channels-BofYHgz8.js.map} +1 -1
  144. package/dist/lib/{shadow-claw-chat-BD18-mAR.js → shadow-claw-chat-Dw32DPV2.js} +12 -12
  145. package/dist/lib/{shadow-claw-chat-BD18-mAR.js.map → shadow-claw-chat-Dw32DPV2.js.map} +1 -1
  146. package/dist/lib/{shadow-claw-control-plane-U6LsfRPm.js → shadow-claw-control-plane-CwFotFDh.js} +7 -7
  147. package/dist/lib/{shadow-claw-control-plane-U6LsfRPm.js.map → shadow-claw-control-plane-CwFotFDh.js.map} +1 -1
  148. package/dist/lib/{shadow-claw-conversations-CDsza_oB.js → shadow-claw-conversations-0UXge23k.js} +5 -5
  149. package/dist/lib/{shadow-claw-conversations-CDsza_oB.js.map → shadow-claw-conversations-0UXge23k.js.map} +1 -1
  150. package/dist/lib/{shadow-claw-file-viewer-CuDhkVcL.js → shadow-claw-file-viewer-CAsn9NxH.js} +10 -10
  151. package/dist/lib/{shadow-claw-file-viewer-CuDhkVcL.js.map → shadow-claw-file-viewer-CAsn9NxH.js.map} +1 -1
  152. package/dist/lib/{shadow-claw-files-BI0P3m8a.js → shadow-claw-files-xGGhsHNH.js} +9 -9
  153. package/dist/lib/{shadow-claw-files-BI0P3m8a.js.map → shadow-claw-files-xGGhsHNH.js.map} +1 -1
  154. package/dist/lib/{shadow-claw-git-xRApLEMN.js → shadow-claw-git-B606hcMU.js} +6 -6
  155. package/dist/lib/{shadow-claw-git-xRApLEMN.js.map → shadow-claw-git-B606hcMU.js.map} +1 -1
  156. package/dist/lib/{shadow-claw-integrations-B2e1Nfg1.js → shadow-claw-integrations-Bwq5cba1.js} +4 -4
  157. package/dist/lib/{shadow-claw-integrations-B2e1Nfg1.js.map → shadow-claw-integrations-Bwq5cba1.js.map} +1 -1
  158. package/dist/lib/{shadow-claw-llm-Bc2mWuGb.js → shadow-claw-llm-BgpcMBIy.js} +6 -6
  159. package/dist/lib/{shadow-claw-llm-Bc2mWuGb.js.map → shadow-claw-llm-BgpcMBIy.js.map} +1 -1
  160. package/dist/lib/{shadow-claw-mcp-remote-D_Bh3aeC.js → shadow-claw-mcp-remote-DvQCA-Bk.js} +4 -4
  161. package/dist/lib/{shadow-claw-mcp-remote-D_Bh3aeC.js.map → shadow-claw-mcp-remote-DvQCA-Bk.js.map} +1 -1
  162. package/dist/lib/{shadow-claw-networking-F5W3n5ci.js → shadow-claw-networking-Cxvf9c7E.js} +2 -2
  163. package/dist/lib/{shadow-claw-networking-F5W3n5ci.js.map → shadow-claw-networking-Cxvf9c7E.js.map} +1 -1
  164. package/dist/lib/{shadow-claw-notifications-lvMt9WJ1.js → shadow-claw-notifications-CGVcFYBy.js} +3 -3
  165. package/dist/lib/{shadow-claw-notifications-lvMt9WJ1.js.map → shadow-claw-notifications-CGVcFYBy.js.map} +1 -1
  166. package/dist/lib/{shadow-claw-page-header-6gq6EMqX.js → shadow-claw-page-header-DAYz3I_o.js} +2 -2
  167. package/dist/lib/{shadow-claw-page-header-6gq6EMqX.js.map → shadow-claw-page-header-DAYz3I_o.js.map} +1 -1
  168. package/dist/lib/{shadow-claw-pages-Bo8_z2S-.js → shadow-claw-pages-BLeEZb9k.js} +24 -12
  169. package/dist/lib/shadow-claw-pages-BLeEZb9k.js.map +1 -0
  170. package/dist/lib/{shadow-claw-peerjs-DRTLCVDQ.js → shadow-claw-peerjs-BcFnbK74.js} +3 -3
  171. package/dist/lib/{shadow-claw-peerjs-DRTLCVDQ.js.map → shadow-claw-peerjs-BcFnbK74.js.map} +1 -1
  172. package/dist/lib/{shadow-claw-settings-D00B4p4L.js → shadow-claw-settings-B7o0Z4hu.js} +9 -9
  173. package/dist/lib/{shadow-claw-settings-D00B4p4L.js.map → shadow-claw-settings-B7o0Z4hu.js.map} +1 -1
  174. package/dist/lib/{shadow-claw-storage-DgMe7-Zo.js → shadow-claw-storage-afMVqB7E.js} +5 -5
  175. package/dist/lib/{shadow-claw-storage-DgMe7-Zo.js.map → shadow-claw-storage-afMVqB7E.js.map} +1 -1
  176. package/dist/lib/{shadow-claw-task-server-D103YOuI.js → shadow-claw-task-server-B9-MQjl6.js} +2 -2
  177. package/dist/lib/{shadow-claw-task-server-D103YOuI.js.map → shadow-claw-task-server-B9-MQjl6.js.map} +1 -1
  178. package/dist/lib/{shadow-claw-tasks-6OhALkq1.js → shadow-claw-tasks-Dc4BC-t-.js} +8 -8
  179. package/dist/lib/{shadow-claw-tasks-6OhALkq1.js.map → shadow-claw-tasks-Dc4BC-t-.js.map} +1 -1
  180. package/dist/lib/{shadow-claw-terminal-BGtxA1qD.js → shadow-claw-terminal-BINcTdsk.js} +2 -2
  181. package/dist/lib/{shadow-claw-terminal-BGtxA1qD.js.map → shadow-claw-terminal-BINcTdsk.js.map} +1 -1
  182. package/dist/lib/{shadow-claw-tools-OeIwAnnd.js → shadow-claw-tools-BjFgXNLR.js} +7 -7
  183. package/dist/lib/{shadow-claw-tools-OeIwAnnd.js.map → shadow-claw-tools-BjFgXNLR.js.map} +1 -1
  184. package/dist/lib/{shadow-claw-webvm-gVFZ197u.js → shadow-claw-webvm-Xe3ByMDw.js} +3 -3
  185. package/dist/lib/{shadow-claw-webvm-gVFZ197u.js.map → shadow-claw-webvm-Xe3ByMDw.js.map} +1 -1
  186. package/dist/lib/subsystems/mcp/webmcp.d.ts +64 -0
  187. package/dist/lib/{syncWebMcpRegistration-Bbph0XZl.js → syncWebMcpRegistration-D7alChz0.js} +21 -6
  188. package/dist/lib/syncWebMcpRegistration-D7alChz0.js.map +1 -0
  189. package/dist/lib/{tools-D0OlSzMD.js → tools-Cmhpk3X8.js} +4 -4
  190. package/dist/lib/{tools-D0OlSzMD.js.map → tools-Cmhpk3X8.js.map} +1 -1
  191. package/dist/lib/utils/index.js +1 -1
  192. package/dist/lib/utils/namespacedStorage.js +1 -1
  193. package/dist/lib/utils/utils.js +1 -1
  194. package/dist/lib/{utils-DYdrO2KI.js → utils-CzgKe_XZ.js} +4 -4
  195. package/dist/lib/{utils-DYdrO2KI.js.map → utils-CzgKe_XZ.js.map} +1 -1
  196. package/dist/lib/{webmcp-D2bocsgh.js → webmcp-oEP9YpR2.js} +119 -3
  197. package/dist/lib/{webmcp-D2bocsgh.js.map → webmcp-oEP9YpR2.js.map} +1 -1
  198. package/dist/public/.well-known/mcp/server-card.json +1 -1
  199. package/dist/public/.well-known/mcp.json +1 -1
  200. package/dist/public/AGENTS.md +8 -2
  201. package/dist/public/README.md +2 -1
  202. package/dist/public/agent.worker.js +1 -1
  203. package/dist/public/app-routes-C6-H2zPH.js +1 -0
  204. package/dist/public/components/shadow-claw/shadow-claw.css +1 -1
  205. package/dist/public/components/shadow-claw/shadow-claw.html +1 -1
  206. package/dist/public/components/shadow-claw-pages/shadow-claw-pages.css +1 -1
  207. package/dist/public/{config-D4lVqMs8.js → config-Dewnqegf.js} +1 -1
  208. package/dist/public/{configurePeerJs-DDOAVSaH.js → configurePeerJs-CaPU2sN5.js} +1 -1
  209. package/dist/public/{connections-CiukjGiH.js → connections-CwE7jE9l.js} +1 -1
  210. package/dist/public/{custom-element-security-piU2U-Ec.js → custom-element-security-DLgHMf5R.js} +1 -1
  211. package/dist/public/docs/README.md +1 -0
  212. package/dist/public/docs/example/article/index.html +4 -4
  213. package/dist/public/docs/guides/publishing-to-github-pages.md +95 -6
  214. package/dist/public/docs/publishing/index.html +4 -4
  215. package/dist/public/docs/skill-creator/index.html +4 -4
  216. package/dist/public/docs/subsystems/pages.md +10 -3
  217. package/dist/public/docs/subsystems/theming.md +242 -0
  218. package/dist/public/docs/subsystems/webmcp.md +89 -1
  219. package/dist/public/{downloadGroupFile-BiZQYY-O.js → downloadGroupFile-ChWHuap_.js} +1 -1
  220. package/dist/public/e2e/README.md +3 -2
  221. package/dist/public/{e2e-bridge-CybB810Q.js → e2e-bridge-BkaldjV2.js} +1 -1
  222. package/dist/public/{file-viewer-DLUBvOVD.js → file-viewer-Df4qUJCn.js} +1 -1
  223. package/dist/public/{getGroupDir-CLz5c8cg.js → getGroupDir-CeSr4fzS.js} +1 -1
  224. package/dist/public/{git-Ba8AeviI.js → git-Bscc1tK-.js} +2 -2
  225. package/dist/public/{iframe-sanitizer-CKOO7cxp.js → iframe-sanitizer-BVdkQsor.js} +1 -1
  226. package/dist/public/{iframe-storage-proxy-BvRiccH0.js → iframe-storage-proxy-Cg8aFw7o.js} +1 -1
  227. package/dist/public/index.css +1 -1
  228. package/dist/public/index.html +4 -4
  229. package/dist/public/index.js +1 -1
  230. package/dist/public/{initChatSplitResize-osJXM2gu.js → initChatSplitResize-C3Rp1LeZ.js} +1 -1
  231. package/dist/public/{initControlPlane-CNggzsRw.js → initControlPlane-C1pnzG16.js} +1 -1
  232. package/dist/public/llms.txt +2 -2
  233. package/dist/public/main/index.html +4 -4
  234. package/dist/public/main/memory/index.html +4 -4
  235. package/dist/public/{markdown-CZA-Ye2Q.js → markdown-D3A902nT.js} +1 -1
  236. package/dist/public/{mcp-reconnect-BK1bYBdC.js → mcp-reconnect-BJWxQBm_.js} +1 -1
  237. package/dist/public/{memoryStorage-Bew1X8Uy.js → memoryStorage-ykv-Ri-y.js} +1 -1
  238. package/dist/public/{orchestrator-D6tel2Zp.js → orchestrator-BVnC4Vtt.js} +2 -2
  239. package/dist/public/{orchestrator-Dc_Q73La.js → orchestrator-CQcc7OFA.js} +2 -2
  240. package/dist/public/{peerjs-CYs1F7sq.js → peerjs-CYC2cI1Y.js} +1 -1
  241. package/dist/public/{push-client-_HUyJlFJ.js → push-client-BJrnOULM.js} +1 -1
  242. package/dist/public/service-worker/fetch-proxy.js +1 -1
  243. package/dist/public/service-worker.js +1 -1
  244. package/dist/public/{shadow-claw-Dabb2AEp.js → shadow-claw-WwfOu-tu.js} +33 -3
  245. package/dist/public/{shadow-claw-accounts-BqGrm47Z.js → shadow-claw-accounts-BFZxrPsH.js} +1 -1
  246. package/dist/public/{shadow-claw-channels-Ci2UPaaA.js → shadow-claw-channels-C0w0c1EF.js} +1 -1
  247. package/dist/public/{shadow-claw-chat-BxR6cBYQ.js → shadow-claw-chat-DIJ0A-QN.js} +1 -1
  248. package/dist/public/{shadow-claw-control-plane-C1A-CZ9H.js → shadow-claw-control-plane-DZoxUd-1.js} +2 -2
  249. package/dist/public/{shadow-claw-conversations-DcwG05-H.js → shadow-claw-conversations-DMyG_5zj.js} +1 -1
  250. package/dist/public/{shadow-claw-file-viewer-CDs1QGXk.js → shadow-claw-file-viewer-DYgZjZb9.js} +1 -1
  251. package/dist/public/{shadow-claw-files-B774Xz1d.js → shadow-claw-files-DIOUdKJr.js} +1 -1
  252. package/dist/public/{shadow-claw-git-CTIPP0vs.js → shadow-claw-git-if5S0QJr.js} +2 -2
  253. package/dist/public/{shadow-claw-integrations-CjtkA6l0.js → shadow-claw-integrations-BQt4hcWe.js} +1 -1
  254. package/dist/public/{shadow-claw-llm-DfmITHde.js → shadow-claw-llm-9uf1KFA0.js} +2 -2
  255. package/dist/public/{shadow-claw-mcp-remote-BPuV22So.js → shadow-claw-mcp-remote-COpNIzJd.js} +1 -1
  256. package/dist/public/{shadow-claw-networking-DEMMygy0.js → shadow-claw-networking-AOVRUn6B.js} +1 -1
  257. package/dist/public/{shadow-claw-notifications-B9Xhcj1P.js → shadow-claw-notifications-CKB-W1TA.js} +1 -1
  258. package/dist/public/{shadow-claw-page-header-CAXokjQy.js → shadow-claw-page-header-D4LmUigA.js} +1 -1
  259. package/dist/public/{shadow-claw-pages-BRQw8bTm.js → shadow-claw-pages-BpHRY4vH.js} +12 -3
  260. package/dist/public/{shadow-claw-settings-BGrDNyqh.js → shadow-claw-settings-CE-aUo_J.js} +3 -3
  261. package/dist/public/{shadow-claw-storage-DK3pf11w.js → shadow-claw-storage-CFKC-Y3V.js} +1 -1
  262. package/dist/public/{shadow-claw-task-server-CMp5Vjza.js → shadow-claw-task-server-B_ZjqK4B.js} +1 -1
  263. package/dist/public/{shadow-claw-tasks-BO8aXM2B.js → shadow-claw-tasks-CGsEv48K.js} +1 -1
  264. package/dist/public/{shadow-claw-terminal-BSeTuyDa.js → shadow-claw-terminal-DcaS7cPH.js} +1 -1
  265. package/dist/public/{shadow-claw-tools-Bj6DLZ-2.js → shadow-claw-tools-Cnu9hKy0.js} +1 -1
  266. package/dist/public/{shadow-claw-webvm-M7d5RH8H.js → shadow-claw-webvm-BB8SFvJ8.js} +1 -1
  267. package/dist/public/syncWebMcpRegistration-D2Zx4oA1.js +1 -0
  268. package/dist/public/theme-init.js +1 -1
  269. package/dist/public/{tools-nysPKnGT.js → tools-sOLCHTj8.js} +1 -1
  270. package/dist/public/{webmcp-Bo3KTgRb.js → webmcp-YFt6vM1_.js} +1 -1
  271. package/dist/server.js +188 -188
  272. package/package.json +1 -1
  273. package/src/cli/build/build.ts +18 -11
  274. package/src/cli/prerender/dsd-shell/prerender-dsd-shell.test.ts +4 -1
  275. package/src/cli/prerender/dsd-shell/prerender-dsd-shell.ts +13 -1
  276. package/src/cli/prerender/pretty-paths/prerender-pretty-paths.ts +22 -0
  277. package/src/cli/prerender/utils/insert-before-closing-head.test.ts +34 -1
  278. package/src/cli/prerender/utils/insert-before-closing-head.ts +20 -6
  279. package/src/cli/site-config/apply.test.ts +90 -0
  280. package/src/cli/site-config/apply.ts +189 -22
  281. package/src/components/shadow-claw/shadow-claw.css +26 -0
  282. package/src/components/shadow-claw/shadow-claw.html +4 -0
  283. package/src/components/shadow-claw/utils/applyRoute.ts +3 -0
  284. package/src/components/shadow-claw/utils/bindEventListeners.test.ts +6 -0
  285. package/src/components/shadow-claw/utils/bindEventListeners.ts +30 -1
  286. package/src/components/shadow-claw/utils/getRoute.test.ts +52 -0
  287. package/src/components/shadow-claw/utils/getRoute.ts +18 -0
  288. package/src/components/shadow-claw/utils/navigateToRoute.test.ts +21 -0
  289. package/src/components/shadow-claw/utils/navigateToRoute.ts +10 -0
  290. package/src/components/shadow-claw/utils/sidebarVisibility.ts +12 -4
  291. package/src/components/shadow-claw/utils/syncSlottedSidebarActiveLinks.test.ts +93 -0
  292. package/src/components/shadow-claw/utils/syncSlottedSidebarActiveLinks.ts +100 -0
  293. package/src/components/shadow-claw-pages/shadow-claw-pages.css +10 -1
  294. package/src/components/shadow-claw-pages/utils/removePreviewIframe.ts +3 -0
  295. package/src/core/orchestrator/utils/syncWebMcpRegistration.test.ts +52 -0
  296. package/src/core/orchestrator/utils/syncWebMcpRegistration.ts +28 -1
  297. package/src/server/middleware/static-files.test.ts +36 -0
  298. package/src/server/middleware/static-files.ts +14 -6
  299. package/src/storage/staticRouting.test.ts +36 -0
  300. package/src/storage/staticRouting.ts +31 -3
  301. package/src/subsystems/mcp/webmcp.test.ts +255 -0
  302. package/src/subsystems/mcp/webmcp.ts +253 -0
  303. package/dist/cli/chunk-00GAzVPl2.js +0 -1
  304. package/dist/cli/chunk-2hnFoNNC.js +0 -1
  305. package/dist/cli/chunk-BKQL4nzF2.js +0 -1
  306. package/dist/cli/chunk-BQ4K3fqj2.js +0 -1
  307. package/dist/cli/chunk-BVCGo6uf2.js +0 -1
  308. package/dist/cli/chunk-Bc9nRNyN.js +0 -11
  309. package/dist/cli/chunk-BcTH_-FL.js +0 -1
  310. package/dist/cli/chunk-BmD_sPRt.js +0 -1
  311. package/dist/cli/chunk-BsxNqqer.js +0 -1
  312. package/dist/cli/chunk-CCcF5T-c.js +0 -1
  313. package/dist/cli/chunk-CFhvwUeL.js +0 -91
  314. package/dist/cli/chunk-CwWtvULn.js +0 -1
  315. package/dist/cli/chunk-DID5VvOG.js +0 -17
  316. package/dist/cli/chunk-Drf-hXIR2.js +0 -1
  317. package/dist/cli/chunk-Dt5HNYVi.js +0 -1
  318. package/dist/cli/chunk-DwZQi9Ax.js +0 -1
  319. package/dist/cli/chunk-m9ZX7TyF2.js +0 -1
  320. package/dist/lib/app-routes-BAEMLu9g.js.map +0 -1
  321. package/dist/lib/shadow-claw-4x3LqlII.js.map +0 -1
  322. package/dist/lib/shadow-claw-pages-Bo8_z2S-.js.map +0 -1
  323. package/dist/lib/syncWebMcpRegistration-Bbph0XZl.js.map +0 -1
  324. package/dist/public/app-routes-BMQ4HFaM.js +0 -1
  325. package/dist/public/syncWebMcpRegistration-4bT0YUyZ.js +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shadow-claw",
3
- "version": "1.40.1",
3
+ "version": "1.41.1",
4
4
  "type": "module",
5
5
  "license": "AGPL-3.0",
6
6
  "description": "ShadowClaw - multi-runtime AI assistant.",
@@ -186,23 +186,26 @@ export async function runBuild(options: RunBuildOptions = {}): Promise<void> {
186
186
 
187
187
  await run("npm run -s rolldown");
188
188
 
189
+ const siteConfigCandidate = await findFirstExistingPath(
190
+ getSiteConfigCandidates(),
191
+ );
192
+ const siteConfigPath = siteConfigCandidate || "pages/site-config.json";
193
+
189
194
  if (prerenderMainMemory) {
190
195
  await prerenderDsdShell({
191
196
  indexPath: "dist/public/index.html",
192
197
  sourcePath: "pages/main",
193
198
  prerenderPages,
199
+ siteConfigPath,
194
200
  });
195
201
  } else {
196
202
  await prerenderDsdShell({
197
203
  indexPath: "dist/public/index.html",
198
204
  noSeed: true,
205
+ siteConfigPath,
199
206
  });
200
207
  }
201
208
 
202
- const siteConfigCandidate = await findFirstExistingPath(
203
- getSiteConfigCandidates(),
204
- );
205
- const siteConfigPath = siteConfigCandidate || "pages/site-config.json";
206
209
  try {
207
210
  await applySiteConfig("dist/public", siteConfigPath);
208
211
  } catch {}
@@ -299,6 +302,7 @@ export async function runBuild(options: RunBuildOptions = {}): Promise<void> {
299
302
  routesPath,
300
303
  sourcePath: "pages/main",
301
304
  prerenderPages,
305
+ siteConfigPath,
302
306
  });
303
307
 
304
308
  await run("npm run -s build:service-worker");
@@ -509,27 +513,29 @@ export async function runBuild(options: RunBuildOptions = {}): Promise<void> {
509
513
  }
510
514
  }
511
515
 
512
- // 7. Prerender DSD Shell
516
+ // 7. Prerender DSD Shell & 8. Apply site-config
517
+ const siteConfigCandidate = await findFirstExistingPath(
518
+ getSiteConfigCandidates(contentRoot),
519
+ );
520
+ const siteConfigPath =
521
+ siteConfigCandidate || join(contentRoot, "pages/site-config.json");
522
+
513
523
  const indexPath = join(distPublicDir, "index.html");
514
524
  if (prerenderMainMemory && (await pathExists(contentPagesMain))) {
515
525
  await prerenderDsdShell({
516
526
  indexPath,
517
527
  sourcePath: contentPagesMain,
518
528
  prerenderPages,
529
+ siteConfigPath,
519
530
  });
520
531
  } else {
521
532
  await prerenderDsdShell({
522
533
  indexPath,
523
534
  noSeed: true,
535
+ siteConfigPath,
524
536
  });
525
537
  }
526
538
 
527
- // 8. Apply site-config
528
- const siteConfigCandidate = await findFirstExistingPath(
529
- getSiteConfigCandidates(contentRoot),
530
- );
531
- const siteConfigPath =
532
- siteConfigCandidate || join(contentRoot, "pages/site-config.json");
533
539
  try {
534
540
  await applySiteConfig(distPublicDir, siteConfigPath);
535
541
  } catch {}
@@ -656,6 +662,7 @@ export async function runBuild(options: RunBuildOptions = {}): Promise<void> {
656
662
  routesPath,
657
663
  sourcePath: mainSourcePath,
658
664
  prerenderPages,
665
+ siteConfigPath,
659
666
  });
660
667
 
661
668
  // 11. Service Worker patch
@@ -502,11 +502,14 @@ describe("prerenderDsdShell purge flag pages", () => {
502
502
 
503
503
  it("applies sidebar visibility config to template", () => {
504
504
  const tpl =
505
- '<li class="nav-item" data-page="chat">Chat</li><li class="nav-item" data-page="files">Files</li>';
505
+ '<li class="nav-item" data-page="chat">Chat</li><li class="nav-item" data-page="files">Files</li><shadow-claw-conversations></shadow-claw-conversations>';
506
506
  const modified = applySidebarVisibilityToTemplate(tpl, {
507
507
  chatHidden: true,
508
508
  });
509
509
  expect(modified).toContain('data-page="chat" hidden aria-hidden="true"');
510
+ expect(modified).toContain(
511
+ '<shadow-claw-conversations hidden aria-hidden="true">',
512
+ );
510
513
  expect(modified).not.toContain('data-page="files" hidden');
511
514
  });
512
515
 
@@ -675,7 +675,13 @@ export function applySidebarVisibilityToTemplate(
675
675
  };
676
676
 
677
677
  if (sidebarConfig.pagesHidden) hidePage("pages");
678
- if (sidebarConfig.chatHidden) hidePage("chat");
678
+ if (sidebarConfig.chatHidden) {
679
+ hidePage("chat");
680
+ next = next.replace(
681
+ /(<shadow-claw-conversations)(?![^>]*\bhidden\b)([^>]*>)/iu,
682
+ '$1 hidden aria-hidden="true"$2',
683
+ );
684
+ }
679
685
  if (sidebarConfig.tasksHidden) hidePage("tasks");
680
686
  if (sidebarConfig.filesHidden) hidePage("files");
681
687
 
@@ -962,7 +968,13 @@ export async function prerenderDsdShell(
962
968
  let siteConfig: any = {};
963
969
  const configCandidatePaths = [
964
970
  options.siteConfigPath,
971
+ path.resolve("shadow-claw-config.json"),
972
+ path.resolve("shadow-claw.config.json"),
973
+ path.resolve("shadowclaw.config.json"),
974
+ path.resolve("pages/shadow-claw-config.json"),
975
+ path.resolve("pages/shadow-claw.config.json"),
965
976
  path.resolve("site-config.json"),
977
+ path.resolve("pages/site-config.json"),
966
978
  ].filter(Boolean) as string[];
967
979
 
968
980
  for (const cfgPath of configCandidatePaths) {
@@ -42,6 +42,7 @@ export interface PrerenderPrettyPathsOptions {
42
42
  indexPath?: string;
43
43
  prerenderPages?: string | number;
44
44
  silent?: boolean;
45
+ siteConfigPath?: string;
45
46
  }
46
47
 
47
48
  export interface PrerenderPrettyPathsResult {
@@ -64,6 +65,25 @@ export async function prerenderPrettyPaths(
64
65
  options.indexPath || path.join(publicDir, "index.html"),
65
66
  );
66
67
 
68
+ let siteConfig: any = {};
69
+ const configCandidatePaths = [
70
+ options.siteConfigPath,
71
+ path.resolve(path.dirname(sourcePath), "shadow-claw.config.json"),
72
+ path.resolve(path.dirname(sourcePath), "site-config.json"),
73
+ path.resolve("shadow-claw.config.json"),
74
+ path.resolve("site-config.json"),
75
+ path.join(publicDir, "shadow-claw.config.json"),
76
+ path.join(publicDir, "site-config.json"),
77
+ ].filter(Boolean) as string[];
78
+
79
+ for (const candidate of configCandidatePaths) {
80
+ try {
81
+ const raw = await readFile(candidate, "utf8");
82
+ siteConfig = JSON.parse(raw);
83
+ break;
84
+ } catch {}
85
+ }
86
+
67
87
  const rawPagesOpt =
68
88
  options.prerenderPages !== undefined
69
89
  ? options.prerenderPages
@@ -360,6 +380,7 @@ export async function prerenderPrettyPaths(
360
380
  shadowClawDsdTemplate = buildShadowClawDsdTemplateWithoutPages(
361
381
  shadowClawTemplateContent,
362
382
  shadowClawCssSource,
383
+ siteConfig.sidebar,
363
384
  );
364
385
  } else {
365
386
  const pagesDsdHost = buildPagesDsdHost(
@@ -375,6 +396,7 @@ export async function prerenderPrettyPaths(
375
396
  shadowClawTemplateContent,
376
397
  pagesDsdHost,
377
398
  shadowClawCssSource,
399
+ siteConfig.sidebar,
378
400
  );
379
401
  }
380
402
 
@@ -1,5 +1,8 @@
1
1
  import { describe, it, expect } from "@jest/globals";
2
- import { insertBeforeClosingHead } from "./insert-before-closing-head.js";
2
+ import {
3
+ findClosingHeadIndex,
4
+ insertBeforeClosingHead,
5
+ } from "./insert-before-closing-head.js";
3
6
 
4
7
  describe("insertBeforeClosingHead", () => {
5
8
  it("inserts content before </head> tag when present", () => {
@@ -16,4 +19,34 @@ describe("insertBeforeClosingHead", () => {
16
19
  const result = insertBeforeClosingHead(html, tag);
17
20
  expect(result.startsWith(`${tag}\n`)).toBe(true);
18
21
  });
22
+
23
+ it("targets the first outer </head> tag when nested templates have their own head", () => {
24
+ const html =
25
+ '<!doctype html><html><head><title>Outer</title></head><body><template shadowrootmode="open"><head><title>Inner</title></head></template></body></html>';
26
+ const tag = '<script id="outer-test">true</script>';
27
+ const result = insertBeforeClosingHead(html, tag);
28
+ const outerIndex = result.indexOf(tag);
29
+ const firstHeadClose = result.indexOf("</head>");
30
+ const secondHeadClose = result.lastIndexOf("</head>");
31
+ expect(outerIndex).toBeLessThan(firstHeadClose);
32
+ expect(firstHeadClose).toBeLessThan(secondHeadClose);
33
+ });
34
+
35
+ it("ignores </head> string literals inside inlined <script> blocks", () => {
36
+ const html =
37
+ '<!doctype html><html><head><title>Outer</title><script>var x = "<head></head>";</script></head><body></body></html>';
38
+ const tag = '<link rel="stylesheet" href="theme.css" />';
39
+ const result = insertBeforeClosingHead(html, tag);
40
+ expect(result).toContain(`${tag}\n</head><body>`);
41
+ expect(findClosingHeadIndex(html)).toBe(html.lastIndexOf("</head>"));
42
+ });
43
+
44
+ it("ignores </head> inside comments and style blocks", () => {
45
+ const html =
46
+ "<!doctype html><html><head><!-- </head> --><style>/* </head> */</style></head><body></body></html>";
47
+ const tag = '<link rel="stylesheet" href="theme.css" />';
48
+ const result = insertBeforeClosingHead(html, tag);
49
+ expect(result).toContain(`${tag}\n</head><body>`);
50
+ expect(findClosingHeadIndex(html)).toBe(html.lastIndexOf("</head>"));
51
+ });
19
52
  });
@@ -1,14 +1,28 @@
1
+ /**
2
+ * Finds the index of the top-level </head> closing tag in an HTML document,
3
+ * skipping any occurrences of "</head>" inside <script>, <style>, <template>,
4
+ * or HTML comments (<!-- ... -->).
5
+ */
6
+ export function findClosingHeadIndex(html: string): number {
7
+ const tokenRegex =
8
+ /<!--[\s\S]*?-->|<script\b[\s\S]*?<\/script>|<style\b[\s\S]*?<\/style>|<template\b[\s\S]*?<\/template>|<\/head>/gi;
9
+ let match: RegExpExecArray | null;
10
+ while ((match = tokenRegex.exec(html)) !== null) {
11
+ if (match[0].toLowerCase().startsWith("</head")) {
12
+ return match.index;
13
+ }
14
+ }
15
+ return -1;
16
+ }
17
+
1
18
  export function insertBeforeClosingHead(
2
19
  html: string,
3
20
  contentToInsert: string,
4
21
  ): string {
5
- const lastHeadIndex = html.lastIndexOf("</head>");
6
- if (lastHeadIndex !== -1) {
22
+ const headIndex = findClosingHeadIndex(html);
23
+ if (headIndex !== -1) {
7
24
  return (
8
- html.slice(0, lastHeadIndex) +
9
- contentToInsert +
10
- "\n" +
11
- html.slice(lastHeadIndex)
25
+ html.slice(0, headIndex) + contentToInsert + "\n" + html.slice(headIndex)
12
26
  );
13
27
  }
14
28
  return `${contentToInsert}\n${html}`;
@@ -202,6 +202,60 @@ describe("apply-site-config", () => {
202
202
  '<link href="favicon.svg" rel="apple-touch-icon" />',
203
203
  );
204
204
  });
205
+
206
+ it("injects slotted sidebar html into shadow-claw light DOM when slotHtml is configured", () => {
207
+ const config = {
208
+ sidebar: {
209
+ slotHtml: '<nav class="custom-nav"><a href="/about">About</a></nav>',
210
+ },
211
+ };
212
+
213
+ const patched = patchIndexHtml(baseHtml, config);
214
+ expect(patched).toContain('slot="sidebar"');
215
+ expect(patched).toContain('<a href="/about">About</a>');
216
+ expect(patched).toContain("<shadow-claw>");
217
+ expect(patched).toContain("</shadow-claw>");
218
+ });
219
+
220
+ it("renders and injects declarative sidebar sections into shadow-claw light DOM", () => {
221
+ const config = {
222
+ sidebar: {
223
+ sections: [
224
+ {
225
+ title: "Home",
226
+ headerHref: "/",
227
+ items: [{ title: "About", href: "/about" }],
228
+ },
229
+ {
230
+ title: "Links",
231
+ items: [
232
+ {
233
+ title: "GitHub",
234
+ href: "https://github.com/xt-ml/shadow-claw",
235
+ target: "_blank",
236
+ },
237
+ ],
238
+ },
239
+ ],
240
+ },
241
+ };
242
+
243
+ const patched = patchIndexHtml(baseHtml, config);
244
+ expect(patched).toContain('slot="sidebar"');
245
+ expect(patched).toContain('class="sidebar-custom-sections"');
246
+ expect(patched).toContain('href="/" class="sidebar-link">Home</a>');
247
+ expect(patched).toContain('href="/about" class="sidebar-link">About</a>');
248
+ expect(patched).toContain(
249
+ 'href="https://github.com/xt-ml/shadow-claw" target="_blank" rel="noopener noreferrer"',
250
+ );
251
+ });
252
+
253
+ it("injects customSidebarHtml parameter directly into shadow-claw light DOM", () => {
254
+ const customHtml =
255
+ '<div slot="sidebar" class="my-custom-sidebar"><p>Hello</p></div>';
256
+ const patched = patchIndexHtml(baseHtml, {}, customHtml);
257
+ expect(patched).toContain(customHtml);
258
+ });
205
259
  });
206
260
 
207
261
  describe("patchManifest", () => {
@@ -756,5 +810,41 @@ https://example.com/about`;
756
810
  ),
757
811
  ).rejects.toThrow();
758
812
  });
813
+
814
+ it("discovers pages/main/sidebar.html and injects it into dist/public/index.html", async () => {
815
+ const distPublicDir = path.join(tmpDir, "dist/public");
816
+ const repoDir = tmpDir;
817
+ const pagesMainDir = path.join(repoDir, "pages/main");
818
+ await mkdir(distPublicDir, { recursive: true });
819
+ await mkdir(pagesMainDir, { recursive: true });
820
+
821
+ const indexPath = path.join(distPublicDir, "index.html");
822
+ await writeFile(
823
+ indexPath,
824
+ "<!doctype html><html><head></head><body><shadow-claw></shadow-claw></body></html>",
825
+ "utf8",
826
+ );
827
+
828
+ const sidebarContent =
829
+ '<nav class="my-sidebar"><a href="/about">About Us</a></nav>';
830
+ await writeFile(
831
+ path.join(pagesMainDir, "sidebar.html"),
832
+ sidebarContent,
833
+ "utf8",
834
+ );
835
+
836
+ const siteConfigPath = path.join(repoDir, "shadow-claw.config.json");
837
+ const config = {
838
+ site: { title: "Custom Sidebar Site" },
839
+ };
840
+ await writeFile(siteConfigPath, JSON.stringify(config), "utf8");
841
+
842
+ const result = await applySiteConfig(distPublicDir, siteConfigPath);
843
+ expect(result.applied).toBe(true);
844
+
845
+ const patchedIndex = await readFile(indexPath, "utf8");
846
+ expect(patchedIndex).toContain('slot="sidebar"');
847
+ expect(patchedIndex).toContain("About Us");
848
+ });
759
849
  });
760
850
  });
@@ -14,6 +14,11 @@ import { fileURLToPath } from "node:url";
14
14
 
15
15
  import { resolveCustomElementScripts } from "./custom-elements/resolve-custom-element-scripts.js";
16
16
  import { sanitizeEmbeddedCustomElementScripts } from "./custom-elements/sanitize-embedded-custom-element-scripts.js";
17
+ import {
18
+ findClosingHeadIndex,
19
+ insertBeforeClosingHead,
20
+ } from "../prerender/utils/insert-before-closing-head.js";
21
+ import { applySidebarVisibilityToTemplate } from "../prerender/dsd-shell/prerender-dsd-shell.js";
17
22
 
18
23
  export interface SiteConfigSite {
19
24
  title?: string;
@@ -57,12 +62,44 @@ export interface SiteConfigPwa {
57
62
  screenshots?: any[];
58
63
  }
59
64
 
65
+ export interface SiteConfigSidebarSectionItem {
66
+ title?: string;
67
+ label?: string;
68
+ href?: string;
69
+ target?: string;
70
+ rel?: string;
71
+ active?: boolean;
72
+ }
73
+
74
+ export interface SiteConfigSidebarSection {
75
+ title?: string;
76
+ headerHref?: string;
77
+ items?: SiteConfigSidebarSectionItem[];
78
+ html?: string;
79
+ }
80
+
81
+ export interface SiteConfigSidebar {
82
+ pagesHidden?: boolean;
83
+ chatHidden?: boolean;
84
+ tasksHidden?: boolean;
85
+ filesHidden?: boolean;
86
+ defaultPage?: string;
87
+ slotHtml?: string;
88
+ slotPath?: string;
89
+ sections?: SiteConfigSidebarSection[];
90
+ }
91
+
60
92
  export interface SiteConfig {
61
93
  site?: SiteConfigSite;
62
94
  branding?: SiteConfigBranding;
63
95
  theme?: SiteConfigTheme;
64
96
  pwa?: SiteConfigPwa;
65
- pages?: { notFoundPath?: string };
97
+ sidebar?: SiteConfigSidebar;
98
+ pages?: {
99
+ notFoundPath?: string;
100
+ sortOrder?: string;
101
+ defaultPinnedPage?: string;
102
+ };
66
103
  notFoundPath?: string;
67
104
  manifestPath?: string;
68
105
  sitemapPath?: string;
@@ -83,21 +120,7 @@ export function escapeHtml(str: string): string {
83
120
  .replace(/'/g, "&#39;");
84
121
  }
85
122
 
86
- export function insertBeforeClosingHead(
87
- html: string,
88
- contentToInsert: string,
89
- ): string {
90
- const lastHeadIndex = html.lastIndexOf("</head>");
91
- if (lastHeadIndex !== -1) {
92
- return (
93
- html.slice(0, lastHeadIndex) +
94
- contentToInsert +
95
- "\n" +
96
- html.slice(lastHeadIndex)
97
- );
98
- }
99
- return `${contentToInsert}\n${html}`;
100
- }
123
+ export { insertBeforeClosingHead, findClosingHeadIndex };
101
124
 
102
125
  const FLATTENED_STYLESHEET_PREFIXES = [
103
126
  "pages/resources/",
@@ -137,7 +160,67 @@ async function writeText(filePath: string, content: string): Promise<void> {
137
160
  await writeFile(filePath, content, "utf8");
138
161
  }
139
162
 
140
- export function patchIndexHtml(html: string, config: SiteConfig): string {
163
+ export function renderSidebarSectionsHtml(
164
+ sections: SiteConfigSidebarSection[],
165
+ ): string {
166
+ if (!Array.isArray(sections) || sections.length === 0) {
167
+ return "";
168
+ }
169
+
170
+ const sectionsHtml = sections
171
+ .map((section, idx) => {
172
+ if (section.html) {
173
+ return section.html;
174
+ }
175
+ const sectionId = section.title
176
+ ? `sidebar-${section.title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`
177
+ : `sidebar-section-${idx + 1}`;
178
+
179
+ const headerContent = section.headerHref
180
+ ? `<a href="${escapeHtml(section.headerHref)}" class="sidebar-link">${escapeHtml(section.title || "")}</a>`
181
+ : `<span>${escapeHtml(section.title || "")}</span>`;
182
+
183
+ const headerHtml = section.title
184
+ ? ` <div class="sidebar-section-header">${headerContent}</div>`
185
+ : "";
186
+
187
+ const itemsHtml = (section.items || [])
188
+ .map((item) => {
189
+ const title = item.title || item.label || "";
190
+ const href = item.href || "#";
191
+ const targetAttr = item.target
192
+ ? ` target="${escapeHtml(item.target)}"`
193
+ : "";
194
+ const relAttr = item.rel
195
+ ? ` rel="${escapeHtml(item.rel)}"`
196
+ : item.target === "_blank"
197
+ ? ' rel="noopener noreferrer"'
198
+ : "";
199
+ const activeClass = item.active ? " active" : "";
200
+ const ariaCurrent = item.active ? ' aria-current="page"' : "";
201
+ return ` <div class="sidebar-item"><a href="${escapeHtml(href)}"${targetAttr}${relAttr} class="sidebar-link${activeClass}"${ariaCurrent}>${escapeHtml(title)}</a></div>`;
202
+ })
203
+ .join("\n");
204
+
205
+ return [
206
+ ` <section class="sidebar-section" id="${sectionId}">`,
207
+ headerHtml,
208
+ itemsHtml,
209
+ ` </section>`,
210
+ ]
211
+ .filter(Boolean)
212
+ .join("\n");
213
+ })
214
+ .join("\n");
215
+
216
+ return `<nav slot="sidebar" class="sidebar-custom-sections" aria-label="Site Navigation">\n${sectionsHtml}\n</nav>`;
217
+ }
218
+
219
+ export function patchIndexHtml(
220
+ html: string,
221
+ config: SiteConfig,
222
+ customSidebarHtml?: string,
223
+ ): string {
141
224
  let next = html;
142
225
 
143
226
  const site = config.site || {};
@@ -265,10 +348,26 @@ export function patchIndexHtml(html: string, config: SiteConfig): string {
265
348
  if (theme.stylesheet) {
266
349
  const stylesheetHref = resolveThemeStylesheetHref(theme.stylesheet);
267
350
  const themeCssTag = `<link rel="stylesheet" href="${escapeHtml(stylesheetHref)}" />`;
268
- if (
269
- !next.includes(`href="${stylesheetHref}"`) &&
270
- !next.includes(`href="${escapeHtml(stylesheetHref)}"`)
271
- ) {
351
+ const escapedHrefPattern = escapeHtml(stylesheetHref).replace(
352
+ /[.*+?^${}()|[\]\\]/g,
353
+ "\\$&",
354
+ );
355
+ const rawHrefPattern = stylesheetHref.replace(
356
+ /[.*+?^${}()|[\]\\]/g,
357
+ "\\$&",
358
+ );
359
+ const linkRegex = new RegExp(
360
+ `<link\\s+[^>]*href=["'](?:${rawHrefPattern}|${escapedHrefPattern})["'][^>]*>`,
361
+ "iu",
362
+ );
363
+ const closingHeadIdx = findClosingHeadIndex(next);
364
+ const headContent =
365
+ closingHeadIdx !== -1 ? next.slice(0, closingHeadIdx) : next;
366
+ const headWithoutScripts = headContent.replace(
367
+ /<script\b[\s\S]*?<\/script>/gi,
368
+ "",
369
+ );
370
+ if (!linkRegex.test(headWithoutScripts)) {
272
371
  if (/<link\s+rel="stylesheet"\s+href="index\.css"\s*\/?>/iu.test(next)) {
273
372
  next = next.replace(
274
373
  /(<link\s+rel="stylesheet"\s+href="index\.css"\s*\/?>)/iu,
@@ -313,6 +412,45 @@ export function patchIndexHtml(html: string, config: SiteConfig): string {
313
412
  next = insertBeforeClosingHead(next, ` ${siteConfigScript}`);
314
413
  }
315
414
 
415
+ const sidebarHtmlToInject =
416
+ customSidebarHtml ||
417
+ config.sidebar?.slotHtml ||
418
+ (config.sidebar?.sections
419
+ ? renderSidebarSectionsHtml(config.sidebar.sections)
420
+ : "");
421
+
422
+ if (sidebarHtmlToInject) {
423
+ let formattedSidebarHtml = sidebarHtmlToInject.trim();
424
+ if (
425
+ !/\bslot="sidebar(?:-nav|-content|-footer)?"/iu.test(formattedSidebarHtml)
426
+ ) {
427
+ formattedSidebarHtml = `<div slot="sidebar" class="sidebar-custom-sections">\n${formattedSidebarHtml}\n</div>`;
428
+ }
429
+
430
+ const existingMatch = next.match(
431
+ /<[^>]+?\bslot="sidebar(?:-nav|-content|-footer)?"[^>]*>[\s\S]*?<\/[^>]+>/iu,
432
+ );
433
+ if (existingMatch) {
434
+ next = next.replace(existingMatch[0], "");
435
+ }
436
+
437
+ if (next.includes("</shadow-claw>")) {
438
+ next = next.replace(
439
+ "</shadow-claw>",
440
+ ` ${formattedSidebarHtml}\n</shadow-claw>`,
441
+ );
442
+ } else if (next.includes("<shadow-claw></shadow-claw>")) {
443
+ next = next.replace(
444
+ "<shadow-claw></shadow-claw>",
445
+ `<shadow-claw>\n ${formattedSidebarHtml}\n</shadow-claw>`,
446
+ );
447
+ }
448
+ }
449
+
450
+ if (config.sidebar) {
451
+ next = applySidebarVisibilityToTemplate(next, config.sidebar);
452
+ }
453
+
316
454
  return next;
317
455
  }
318
456
 
@@ -993,10 +1131,39 @@ export async function applySiteConfig(
993
1131
 
994
1132
  await copyCustomSiteFiles(config, distPublicDir, siteConfigPath);
995
1133
 
1134
+ const configDir = siteConfigPath
1135
+ ? path.dirname(path.resolve(siteConfigPath))
1136
+ : process.cwd();
1137
+ const templateRootDir = getTemplateRootDir(configDir);
1138
+
1139
+ let sidebarHtml: string | undefined;
1140
+ if (config.sidebar?.slotHtml) {
1141
+ sidebarHtml = config.sidebar.slotHtml;
1142
+ } else if (config.sidebar?.sections) {
1143
+ sidebarHtml = renderSidebarSectionsHtml(config.sidebar.sections);
1144
+ } else {
1145
+ const sidebarCandidates = [
1146
+ config.sidebar?.slotPath,
1147
+ "pages/main/sidebar.html",
1148
+ "pages/sidebar.html",
1149
+ "sidebar.html",
1150
+ "resources/sidebar.html",
1151
+ ].filter(Boolean) as string[];
1152
+
1153
+ const candidatePaths = sidebarCandidates.flatMap((c) =>
1154
+ getCandidateFilePaths(c, configDir, templateRootDir),
1155
+ );
1156
+ const foundSidebar = await findFirstExisting(candidatePaths);
1157
+ if (foundSidebar) {
1158
+ sidebarHtml = (await readText(foundSidebar)) ?? undefined;
1159
+ console.log(` Found slotted sidebar: ${foundSidebar}`);
1160
+ }
1161
+ }
1162
+
996
1163
  const indexPath = path.join(distPublicDir, "index.html");
997
1164
  const indexHtml = await readText(indexPath);
998
1165
  if (indexHtml) {
999
- const patched = patchIndexHtml(indexHtml, config);
1166
+ const patched = patchIndexHtml(indexHtml, config, sidebarHtml);
1000
1167
  await writeText(indexPath, patched);
1001
1168
  console.log(" Patched index.html");
1002
1169
  }
@@ -90,6 +90,10 @@ a:hover {
90
90
  transition: transform 0.2s ease;
91
91
  }
92
92
 
93
+ .header-main-toggle {
94
+ display: var(--shadow-claw-header-main-toggle-display, block);
95
+ }
96
+
93
97
  .header-main-toggle[aria-pressed="true"],
94
98
  .activity-log-toggle[aria-pressed="true"] {
95
99
  box-shadow: 0 0 0.5rem
@@ -279,6 +283,28 @@ a:hover {
279
283
  padding: 0.5rem;
280
284
  }
281
285
 
286
+ .nav-menu:not(:has(.nav-item:not([hidden]))) {
287
+ display: none;
288
+ }
289
+
290
+ slot[name="sidebar"],
291
+ slot[name="sidebar-nav"],
292
+ slot[name="sidebar-content"],
293
+ slot[name="sidebar-footer"] {
294
+ display: block;
295
+ }
296
+
297
+ ::slotted([slot="sidebar"]),
298
+ ::slotted([slot="sidebar-nav"]),
299
+ ::slotted([slot="sidebar-content"]),
300
+ ::slotted([slot="sidebar-footer"]) {
301
+ display: block;
302
+ }
303
+
304
+ .sidebar-footer {
305
+ display: var(--shadow-claw-sidebar-footer-display, block);
306
+ }
307
+
282
308
  .sidebar-spacer {
283
309
  flex: 1;
284
310
  }