shadow-claw 1.40.1 → 1.41.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 (319) hide show
  1. package/.well-known/mcp/server-card.json +1 -1
  2. package/.well-known/mcp.json +1 -1
  3. package/README.md +1 -0
  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-BXg4-9Rq2.js → chunk-8CrZyP1b2.js} +1 -1
  8. package/dist/cli/{chunk-BP5Hg6CM.js → chunk-B--ueArB.js} +1 -1
  9. package/dist/cli/chunk-BIh7rXlJ2.js +1 -0
  10. package/dist/cli/{chunk-Drr0VSGA2.js → chunk-BKnTOLLS2.js} +1 -1
  11. package/dist/cli/{chunk-C5PuRV08.js → chunk-BLMC1Bxv.js} +1 -1
  12. package/dist/cli/{chunk-B2tNSlFy2.js → chunk-BOigzz2U2.js} +1 -1
  13. package/dist/cli/{chunk-DOf3DZdR.js → chunk-BOzn335y.js} +1 -1
  14. package/dist/cli/chunk-BjOeVLb42.js +1 -0
  15. package/dist/cli/{chunk-DqTx0rRp2.js → chunk-Bjqh5_SU2.js} +1 -1
  16. package/dist/cli/chunk-BqWBd_jF2.js +1 -0
  17. package/dist/cli/{chunk-Bc9nRNyN.js → chunk-BvVUnHTq.js} +12 -9
  18. package/dist/cli/{chunk-DIbSoQmd2.js → chunk-C6FFByBU2.js} +2 -2
  19. package/dist/cli/{chunk-Drq18VZe.js → chunk-C9-aL3rF.js} +1 -1
  20. package/dist/cli/{chunk-BUhqHeEA2.js → chunk-C9czddEh2.js} +2 -2
  21. package/dist/cli/chunk-CCx8WOwz2.js +1 -0
  22. package/dist/cli/{chunk-BkMSjVJb.js → chunk-CK6ejMig.js} +1 -1
  23. package/dist/cli/chunk-CUUERioR2.js +1 -0
  24. package/dist/cli/chunk-CXAobnJd.js +1 -0
  25. package/dist/cli/chunk-CY1X0pOF2.js +1 -0
  26. package/dist/cli/chunk-CZvvgLVv.js +1 -0
  27. package/dist/cli/chunk-CbcwbfJB.js +1 -0
  28. package/dist/cli/{chunk-quilB3TV2.js → chunk-Ci9OJGcd2.js} +1 -1
  29. package/dist/cli/chunk-Cj9wl0qx.js +1 -0
  30. package/dist/cli/{chunk-3gRhOmGo2.js → chunk-CjJsVrKI2.js} +1 -1
  31. package/dist/cli/{chunk-CmEfzm-_.js → chunk-ClPZ4Rea.js} +1 -1
  32. package/dist/cli/{chunk-WMPAfIvV2.js → chunk-DHJCGRuG2.js} +1 -1
  33. package/dist/cli/{chunk-C7Y2xIk4.js → chunk-DKVkYTV9.js} +1 -1
  34. package/dist/cli/chunk-DR2XtWRo.js +1 -0
  35. package/dist/cli/{chunk-DID5VvOG.js → chunk-DTES-dE6.js} +3 -3
  36. package/dist/cli/{chunk-BiVJVh_S.js → chunk-DUlIqkaX.js} +7 -7
  37. package/dist/cli/{chunk-CkpueGaZ2.js → chunk-DjWle8FF2.js} +1 -1
  38. package/dist/cli/{chunk-CSvHmNOk.js → chunk-DmRXVLfy.js} +1 -1
  39. package/dist/cli/{chunk-BT1xXZn72.js → chunk-HlSiTC-u2.js} +1 -1
  40. package/dist/cli/{chunk-da0bsiOn.js → chunk-P4JSeFBI.js} +1 -1
  41. package/dist/cli/chunk-UcNbHaRQ.js +1 -0
  42. package/dist/cli/{chunk-BXFHFbEd.js → chunk-UweLmfQS.js} +1 -1
  43. package/dist/cli/chunk-bQQkVCw-.js +94 -0
  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 +2 -1
  201. package/dist/public/README.md +1 -0
  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/example/article/index.html +4 -4
  212. package/dist/public/docs/guides/publishing-to-github-pages.md +49 -6
  213. package/dist/public/docs/publishing/index.html +4 -4
  214. package/dist/public/docs/skill-creator/index.html +4 -4
  215. package/dist/public/docs/subsystems/pages.md +10 -3
  216. package/dist/public/docs/subsystems/webmcp.md +89 -1
  217. package/dist/public/{downloadGroupFile-BiZQYY-O.js → downloadGroupFile-ChWHuap_.js} +1 -1
  218. package/dist/public/e2e/README.md +2 -2
  219. package/dist/public/{e2e-bridge-CybB810Q.js → e2e-bridge-BkaldjV2.js} +1 -1
  220. package/dist/public/{file-viewer-DLUBvOVD.js → file-viewer-Df4qUJCn.js} +1 -1
  221. package/dist/public/{getGroupDir-CLz5c8cg.js → getGroupDir-CeSr4fzS.js} +1 -1
  222. package/dist/public/{git-Ba8AeviI.js → git-Bscc1tK-.js} +2 -2
  223. package/dist/public/{iframe-sanitizer-CKOO7cxp.js → iframe-sanitizer-BVdkQsor.js} +1 -1
  224. package/dist/public/{iframe-storage-proxy-BvRiccH0.js → iframe-storage-proxy-Cg8aFw7o.js} +1 -1
  225. package/dist/public/index.css +1 -1
  226. package/dist/public/index.html +4 -4
  227. package/dist/public/index.js +1 -1
  228. package/dist/public/{initChatSplitResize-osJXM2gu.js → initChatSplitResize-C3Rp1LeZ.js} +1 -1
  229. package/dist/public/{initControlPlane-CNggzsRw.js → initControlPlane-C1pnzG16.js} +1 -1
  230. package/dist/public/llms.txt +1 -1
  231. package/dist/public/main/index.html +4 -4
  232. package/dist/public/main/memory/index.html +4 -4
  233. package/dist/public/{markdown-CZA-Ye2Q.js → markdown-D3A902nT.js} +1 -1
  234. package/dist/public/{mcp-reconnect-BK1bYBdC.js → mcp-reconnect-BJWxQBm_.js} +1 -1
  235. package/dist/public/{memoryStorage-Bew1X8Uy.js → memoryStorage-ykv-Ri-y.js} +1 -1
  236. package/dist/public/{orchestrator-D6tel2Zp.js → orchestrator-BVnC4Vtt.js} +2 -2
  237. package/dist/public/{orchestrator-Dc_Q73La.js → orchestrator-CQcc7OFA.js} +2 -2
  238. package/dist/public/{peerjs-CYs1F7sq.js → peerjs-CYC2cI1Y.js} +1 -1
  239. package/dist/public/{push-client-_HUyJlFJ.js → push-client-BJrnOULM.js} +1 -1
  240. package/dist/public/service-worker/fetch-proxy.js +1 -1
  241. package/dist/public/service-worker.js +1 -1
  242. package/dist/public/{shadow-claw-Dabb2AEp.js → shadow-claw-WwfOu-tu.js} +33 -3
  243. package/dist/public/{shadow-claw-accounts-BqGrm47Z.js → shadow-claw-accounts-BFZxrPsH.js} +1 -1
  244. package/dist/public/{shadow-claw-channels-Ci2UPaaA.js → shadow-claw-channels-C0w0c1EF.js} +1 -1
  245. package/dist/public/{shadow-claw-chat-BxR6cBYQ.js → shadow-claw-chat-DIJ0A-QN.js} +1 -1
  246. package/dist/public/{shadow-claw-control-plane-C1A-CZ9H.js → shadow-claw-control-plane-DZoxUd-1.js} +2 -2
  247. package/dist/public/{shadow-claw-conversations-DcwG05-H.js → shadow-claw-conversations-DMyG_5zj.js} +1 -1
  248. package/dist/public/{shadow-claw-file-viewer-CDs1QGXk.js → shadow-claw-file-viewer-DYgZjZb9.js} +1 -1
  249. package/dist/public/{shadow-claw-files-B774Xz1d.js → shadow-claw-files-DIOUdKJr.js} +1 -1
  250. package/dist/public/{shadow-claw-git-CTIPP0vs.js → shadow-claw-git-if5S0QJr.js} +2 -2
  251. package/dist/public/{shadow-claw-integrations-CjtkA6l0.js → shadow-claw-integrations-BQt4hcWe.js} +1 -1
  252. package/dist/public/{shadow-claw-llm-DfmITHde.js → shadow-claw-llm-9uf1KFA0.js} +2 -2
  253. package/dist/public/{shadow-claw-mcp-remote-BPuV22So.js → shadow-claw-mcp-remote-COpNIzJd.js} +1 -1
  254. package/dist/public/{shadow-claw-networking-DEMMygy0.js → shadow-claw-networking-AOVRUn6B.js} +1 -1
  255. package/dist/public/{shadow-claw-notifications-B9Xhcj1P.js → shadow-claw-notifications-CKB-W1TA.js} +1 -1
  256. package/dist/public/{shadow-claw-page-header-CAXokjQy.js → shadow-claw-page-header-D4LmUigA.js} +1 -1
  257. package/dist/public/{shadow-claw-pages-BRQw8bTm.js → shadow-claw-pages-BpHRY4vH.js} +12 -3
  258. package/dist/public/{shadow-claw-settings-BGrDNyqh.js → shadow-claw-settings-CE-aUo_J.js} +3 -3
  259. package/dist/public/{shadow-claw-storage-DK3pf11w.js → shadow-claw-storage-CFKC-Y3V.js} +1 -1
  260. package/dist/public/{shadow-claw-task-server-CMp5Vjza.js → shadow-claw-task-server-B_ZjqK4B.js} +1 -1
  261. package/dist/public/{shadow-claw-tasks-BO8aXM2B.js → shadow-claw-tasks-CGsEv48K.js} +1 -1
  262. package/dist/public/{shadow-claw-terminal-BSeTuyDa.js → shadow-claw-terminal-DcaS7cPH.js} +1 -1
  263. package/dist/public/{shadow-claw-tools-Bj6DLZ-2.js → shadow-claw-tools-Cnu9hKy0.js} +1 -1
  264. package/dist/public/{shadow-claw-webvm-M7d5RH8H.js → shadow-claw-webvm-BB8SFvJ8.js} +1 -1
  265. package/dist/public/syncWebMcpRegistration-D2Zx4oA1.js +1 -0
  266. package/dist/public/theme-init.js +1 -1
  267. package/dist/public/{tools-nysPKnGT.js → tools-sOLCHTj8.js} +1 -1
  268. package/dist/public/{webmcp-Bo3KTgRb.js → webmcp-YFt6vM1_.js} +1 -1
  269. package/dist/server.js +188 -188
  270. package/package.json +1 -1
  271. package/src/cli/prerender/dsd-shell/prerender-dsd-shell.test.ts +4 -1
  272. package/src/cli/prerender/dsd-shell/prerender-dsd-shell.ts +7 -1
  273. package/src/cli/prerender/utils/insert-before-closing-head.test.ts +12 -0
  274. package/src/cli/prerender/utils/insert-before-closing-head.ts +4 -6
  275. package/src/cli/site-config/apply.test.ts +90 -0
  276. package/src/cli/site-config/apply.ts +181 -13
  277. package/src/components/shadow-claw/shadow-claw.css +26 -0
  278. package/src/components/shadow-claw/shadow-claw.html +4 -0
  279. package/src/components/shadow-claw/utils/applyRoute.ts +3 -0
  280. package/src/components/shadow-claw/utils/bindEventListeners.test.ts +6 -0
  281. package/src/components/shadow-claw/utils/bindEventListeners.ts +30 -1
  282. package/src/components/shadow-claw/utils/getRoute.test.ts +52 -0
  283. package/src/components/shadow-claw/utils/getRoute.ts +18 -0
  284. package/src/components/shadow-claw/utils/navigateToRoute.test.ts +21 -0
  285. package/src/components/shadow-claw/utils/navigateToRoute.ts +10 -0
  286. package/src/components/shadow-claw/utils/sidebarVisibility.ts +12 -4
  287. package/src/components/shadow-claw/utils/syncSlottedSidebarActiveLinks.test.ts +93 -0
  288. package/src/components/shadow-claw/utils/syncSlottedSidebarActiveLinks.ts +100 -0
  289. package/src/components/shadow-claw-pages/shadow-claw-pages.css +10 -1
  290. package/src/components/shadow-claw-pages/utils/removePreviewIframe.ts +3 -0
  291. package/src/core/orchestrator/utils/syncWebMcpRegistration.test.ts +52 -0
  292. package/src/core/orchestrator/utils/syncWebMcpRegistration.ts +28 -1
  293. package/src/server/middleware/static-files.test.ts +36 -0
  294. package/src/server/middleware/static-files.ts +14 -6
  295. package/src/storage/staticRouting.test.ts +36 -0
  296. package/src/storage/staticRouting.ts +31 -3
  297. package/src/subsystems/mcp/webmcp.test.ts +255 -0
  298. package/src/subsystems/mcp/webmcp.ts +253 -0
  299. package/dist/cli/chunk-00GAzVPl2.js +0 -1
  300. package/dist/cli/chunk-2hnFoNNC.js +0 -1
  301. package/dist/cli/chunk-BKQL4nzF2.js +0 -1
  302. package/dist/cli/chunk-BQ4K3fqj2.js +0 -1
  303. package/dist/cli/chunk-BVCGo6uf2.js +0 -1
  304. package/dist/cli/chunk-BcTH_-FL.js +0 -1
  305. package/dist/cli/chunk-BmD_sPRt.js +0 -1
  306. package/dist/cli/chunk-BsxNqqer.js +0 -1
  307. package/dist/cli/chunk-CCcF5T-c.js +0 -1
  308. package/dist/cli/chunk-CFhvwUeL.js +0 -91
  309. package/dist/cli/chunk-CwWtvULn.js +0 -1
  310. package/dist/cli/chunk-Drf-hXIR2.js +0 -1
  311. package/dist/cli/chunk-Dt5HNYVi.js +0 -1
  312. package/dist/cli/chunk-DwZQi9Ax.js +0 -1
  313. package/dist/cli/chunk-m9ZX7TyF2.js +0 -1
  314. package/dist/lib/app-routes-BAEMLu9g.js.map +0 -1
  315. package/dist/lib/shadow-claw-4x3LqlII.js.map +0 -1
  316. package/dist/lib/shadow-claw-pages-Bo8_z2S-.js.map +0 -1
  317. package/dist/lib/syncWebMcpRegistration-Bbph0XZl.js.map +0 -1
  318. package/dist/public/app-routes-BMQ4HFaM.js +0 -1
  319. 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.0",
4
4
  "type": "module",
5
5
  "license": "AGPL-3.0",
6
6
  "description": "ShadowClaw - multi-runtime AI assistant.",
@@ -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
 
@@ -16,4 +16,16 @@ describe("insertBeforeClosingHead", () => {
16
16
  const result = insertBeforeClosingHead(html, tag);
17
17
  expect(result.startsWith(`${tag}\n`)).toBe(true);
18
18
  });
19
+
20
+ it("targets the first outer </head> tag when nested templates have their own head", () => {
21
+ const html =
22
+ '<!doctype html><html><head><title>Outer</title></head><body><template shadowrootmode="open"><head><title>Inner</title></head></template></body></html>';
23
+ const tag = '<script id="outer-test">true</script>';
24
+ const result = insertBeforeClosingHead(html, tag);
25
+ const outerIndex = result.indexOf(tag);
26
+ const firstHeadClose = result.indexOf("</head>");
27
+ const secondHeadClose = result.lastIndexOf("</head>");
28
+ expect(outerIndex).toBeLessThan(firstHeadClose);
29
+ expect(firstHeadClose).toBeLessThan(secondHeadClose);
30
+ });
19
31
  });
@@ -2,13 +2,11 @@ export function insertBeforeClosingHead(
2
2
  html: string,
3
3
  contentToInsert: string,
4
4
  ): string {
5
- const lastHeadIndex = html.lastIndexOf("</head>");
6
- if (lastHeadIndex !== -1) {
5
+ const headMatch = /<\/head>/i.exec(html);
6
+ if (headMatch) {
7
+ const headIndex = headMatch.index;
7
8
  return (
8
- html.slice(0, lastHeadIndex) +
9
- contentToInsert +
10
- "\n" +
11
- html.slice(lastHeadIndex)
9
+ html.slice(0, headIndex) + contentToInsert + "\n" + html.slice(headIndex)
12
10
  );
13
11
  }
14
12
  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
  });
@@ -57,12 +57,44 @@ export interface SiteConfigPwa {
57
57
  screenshots?: any[];
58
58
  }
59
59
 
60
+ export interface SiteConfigSidebarSectionItem {
61
+ title?: string;
62
+ label?: string;
63
+ href?: string;
64
+ target?: string;
65
+ rel?: string;
66
+ active?: boolean;
67
+ }
68
+
69
+ export interface SiteConfigSidebarSection {
70
+ title?: string;
71
+ headerHref?: string;
72
+ items?: SiteConfigSidebarSectionItem[];
73
+ html?: string;
74
+ }
75
+
76
+ export interface SiteConfigSidebar {
77
+ pagesHidden?: boolean;
78
+ chatHidden?: boolean;
79
+ tasksHidden?: boolean;
80
+ filesHidden?: boolean;
81
+ defaultPage?: string;
82
+ slotHtml?: string;
83
+ slotPath?: string;
84
+ sections?: SiteConfigSidebarSection[];
85
+ }
86
+
60
87
  export interface SiteConfig {
61
88
  site?: SiteConfigSite;
62
89
  branding?: SiteConfigBranding;
63
90
  theme?: SiteConfigTheme;
64
91
  pwa?: SiteConfigPwa;
65
- pages?: { notFoundPath?: string };
92
+ sidebar?: SiteConfigSidebar;
93
+ pages?: {
94
+ notFoundPath?: string;
95
+ sortOrder?: string;
96
+ defaultPinnedPage?: string;
97
+ };
66
98
  notFoundPath?: string;
67
99
  manifestPath?: string;
68
100
  sitemapPath?: string;
@@ -87,13 +119,11 @@ export function insertBeforeClosingHead(
87
119
  html: string,
88
120
  contentToInsert: string,
89
121
  ): string {
90
- const lastHeadIndex = html.lastIndexOf("</head>");
91
- if (lastHeadIndex !== -1) {
122
+ const headMatch = /<\/head>/i.exec(html);
123
+ if (headMatch) {
124
+ const headIndex = headMatch.index;
92
125
  return (
93
- html.slice(0, lastHeadIndex) +
94
- contentToInsert +
95
- "\n" +
96
- html.slice(lastHeadIndex)
126
+ html.slice(0, headIndex) + contentToInsert + "\n" + html.slice(headIndex)
97
127
  );
98
128
  }
99
129
  return `${contentToInsert}\n${html}`;
@@ -137,7 +167,67 @@ async function writeText(filePath: string, content: string): Promise<void> {
137
167
  await writeFile(filePath, content, "utf8");
138
168
  }
139
169
 
140
- export function patchIndexHtml(html: string, config: SiteConfig): string {
170
+ export function renderSidebarSectionsHtml(
171
+ sections: SiteConfigSidebarSection[],
172
+ ): string {
173
+ if (!Array.isArray(sections) || sections.length === 0) {
174
+ return "";
175
+ }
176
+
177
+ const sectionsHtml = sections
178
+ .map((section, idx) => {
179
+ if (section.html) {
180
+ return section.html;
181
+ }
182
+ const sectionId = section.title
183
+ ? `sidebar-${section.title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`
184
+ : `sidebar-section-${idx + 1}`;
185
+
186
+ const headerContent = section.headerHref
187
+ ? `<a href="${escapeHtml(section.headerHref)}" class="sidebar-link">${escapeHtml(section.title || "")}</a>`
188
+ : `<span>${escapeHtml(section.title || "")}</span>`;
189
+
190
+ const headerHtml = section.title
191
+ ? ` <div class="sidebar-section-header">${headerContent}</div>`
192
+ : "";
193
+
194
+ const itemsHtml = (section.items || [])
195
+ .map((item) => {
196
+ const title = item.title || item.label || "";
197
+ const href = item.href || "#";
198
+ const targetAttr = item.target
199
+ ? ` target="${escapeHtml(item.target)}"`
200
+ : "";
201
+ const relAttr = item.rel
202
+ ? ` rel="${escapeHtml(item.rel)}"`
203
+ : item.target === "_blank"
204
+ ? ' rel="noopener noreferrer"'
205
+ : "";
206
+ const activeClass = item.active ? " active" : "";
207
+ const ariaCurrent = item.active ? ' aria-current="page"' : "";
208
+ return ` <div class="sidebar-item"><a href="${escapeHtml(href)}"${targetAttr}${relAttr} class="sidebar-link${activeClass}"${ariaCurrent}>${escapeHtml(title)}</a></div>`;
209
+ })
210
+ .join("\n");
211
+
212
+ return [
213
+ ` <section class="sidebar-section" id="${sectionId}">`,
214
+ headerHtml,
215
+ itemsHtml,
216
+ ` </section>`,
217
+ ]
218
+ .filter(Boolean)
219
+ .join("\n");
220
+ })
221
+ .join("\n");
222
+
223
+ return `<nav slot="sidebar" class="sidebar-custom-sections" aria-label="Site Navigation">\n${sectionsHtml}\n</nav>`;
224
+ }
225
+
226
+ export function patchIndexHtml(
227
+ html: string,
228
+ config: SiteConfig,
229
+ customSidebarHtml?: string,
230
+ ): string {
141
231
  let next = html;
142
232
 
143
233
  const site = config.site || {};
@@ -265,10 +355,24 @@ export function patchIndexHtml(html: string, config: SiteConfig): string {
265
355
  if (theme.stylesheet) {
266
356
  const stylesheetHref = resolveThemeStylesheetHref(theme.stylesheet);
267
357
  const themeCssTag = `<link rel="stylesheet" href="${escapeHtml(stylesheetHref)}" />`;
268
- if (
269
- !next.includes(`href="${stylesheetHref}"`) &&
270
- !next.includes(`href="${escapeHtml(stylesheetHref)}"`)
271
- ) {
358
+ const escapedHrefPattern = escapeHtml(stylesheetHref).replace(
359
+ /[.*+?^${}()|[\]\\]/g,
360
+ "\\$&",
361
+ );
362
+ const rawHrefPattern = stylesheetHref.replace(
363
+ /[.*+?^${}()|[\]\\]/g,
364
+ "\\$&",
365
+ );
366
+ const linkRegex = new RegExp(
367
+ `<link\\s+[^>]*href=["'](?:${rawHrefPattern}|${escapedHrefPattern})["'][^>]*>`,
368
+ "iu",
369
+ );
370
+ const headContent = next.slice(0, Math.max(0, next.indexOf("</head>")));
371
+ const headWithoutScripts = headContent.replace(
372
+ /<script\b[\s\S]*?<\/script>/gi,
373
+ "",
374
+ );
375
+ if (!linkRegex.test(headWithoutScripts)) {
272
376
  if (/<link\s+rel="stylesheet"\s+href="index\.css"\s*\/?>/iu.test(next)) {
273
377
  next = next.replace(
274
378
  /(<link\s+rel="stylesheet"\s+href="index\.css"\s*\/?>)/iu,
@@ -313,6 +417,41 @@ export function patchIndexHtml(html: string, config: SiteConfig): string {
313
417
  next = insertBeforeClosingHead(next, ` ${siteConfigScript}`);
314
418
  }
315
419
 
420
+ const sidebarHtmlToInject =
421
+ customSidebarHtml ||
422
+ config.sidebar?.slotHtml ||
423
+ (config.sidebar?.sections
424
+ ? renderSidebarSectionsHtml(config.sidebar.sections)
425
+ : "");
426
+
427
+ if (sidebarHtmlToInject) {
428
+ let formattedSidebarHtml = sidebarHtmlToInject.trim();
429
+ if (
430
+ !/\bslot="sidebar(?:-nav|-content|-footer)?"/iu.test(formattedSidebarHtml)
431
+ ) {
432
+ formattedSidebarHtml = `<div slot="sidebar" class="sidebar-custom-sections">\n${formattedSidebarHtml}\n</div>`;
433
+ }
434
+
435
+ const existingMatch = next.match(
436
+ /<[^>]+?\bslot="sidebar(?:-nav|-content|-footer)?"[^>]*>[\s\S]*?<\/[^>]+>/iu,
437
+ );
438
+ if (existingMatch) {
439
+ next = next.replace(existingMatch[0], "");
440
+ }
441
+
442
+ if (next.includes("</shadow-claw>")) {
443
+ next = next.replace(
444
+ "</shadow-claw>",
445
+ ` ${formattedSidebarHtml}\n</shadow-claw>`,
446
+ );
447
+ } else if (next.includes("<shadow-claw></shadow-claw>")) {
448
+ next = next.replace(
449
+ "<shadow-claw></shadow-claw>",
450
+ `<shadow-claw>\n ${formattedSidebarHtml}\n</shadow-claw>`,
451
+ );
452
+ }
453
+ }
454
+
316
455
  return next;
317
456
  }
318
457
 
@@ -993,10 +1132,39 @@ export async function applySiteConfig(
993
1132
 
994
1133
  await copyCustomSiteFiles(config, distPublicDir, siteConfigPath);
995
1134
 
1135
+ const configDir = siteConfigPath
1136
+ ? path.dirname(path.resolve(siteConfigPath))
1137
+ : process.cwd();
1138
+ const templateRootDir = getTemplateRootDir(configDir);
1139
+
1140
+ let sidebarHtml: string | undefined;
1141
+ if (config.sidebar?.slotHtml) {
1142
+ sidebarHtml = config.sidebar.slotHtml;
1143
+ } else if (config.sidebar?.sections) {
1144
+ sidebarHtml = renderSidebarSectionsHtml(config.sidebar.sections);
1145
+ } else {
1146
+ const sidebarCandidates = [
1147
+ config.sidebar?.slotPath,
1148
+ "pages/main/sidebar.html",
1149
+ "pages/sidebar.html",
1150
+ "sidebar.html",
1151
+ "resources/sidebar.html",
1152
+ ].filter(Boolean) as string[];
1153
+
1154
+ const candidatePaths = sidebarCandidates.flatMap((c) =>
1155
+ getCandidateFilePaths(c, configDir, templateRootDir),
1156
+ );
1157
+ const foundSidebar = await findFirstExisting(candidatePaths);
1158
+ if (foundSidebar) {
1159
+ sidebarHtml = (await readText(foundSidebar)) ?? undefined;
1160
+ console.log(` Found slotted sidebar: ${foundSidebar}`);
1161
+ }
1162
+ }
1163
+
996
1164
  const indexPath = path.join(distPublicDir, "index.html");
997
1165
  const indexHtml = await readText(indexPath);
998
1166
  if (indexHtml) {
999
- const patched = patchIndexHtml(indexHtml, config);
1167
+ const patched = patchIndexHtml(indexHtml, config, sidebarHtml);
1000
1168
  await writeText(indexPath, patched);
1001
1169
  console.log(" Patched index.html");
1002
1170
  }
@@ -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
  }
@@ -136,8 +136,12 @@
136
136
  <li class="nav-item" data-page="files">📁 Files</li>
137
137
  </ul>
138
138
  </nav>
139
+ <slot name="sidebar-nav"></slot>
140
+ <slot name="sidebar"></slot>
141
+ <slot name="sidebar-content"></slot>
139
142
  <div class="sidebar-spacer"></div>
140
143
  <shadow-claw-conversations></shadow-claw-conversations>
144
+ <slot name="sidebar-footer"></slot>
141
145
  <div class="sidebar-footer">
142
146
  <button class="settings-btn" data-action="show-settings">
143
147
  ⚙️ Settings
@@ -2,6 +2,7 @@ import { DEFAULT_GROUP_ID } from "../../../config/config.js";
2
2
 
3
3
  import { applyAnchorWithRetry } from "./applyAnchorWithRetry.js";
4
4
  import { showPage } from "./showPage.js";
5
+ import { syncSlottedSidebarActiveLinks } from "./syncSlottedSidebarActiveLinks.js";
5
6
 
6
7
  import type { ShadowClawDatabase } from "../../../db/types.js";
7
8
  import type { ShadowClawAppRoute } from "../../../core/app-routes.js";
@@ -139,4 +140,6 @@ export async function applyRoute(
139
140
  }
140
141
  }
141
142
  }
143
+
144
+ syncSlottedSidebarActiveLinks(shadowClaw);
142
145
  }
@@ -817,6 +817,12 @@ describe("bindEventListeners", () => {
817
817
  oStore,
818
818
  new URL("https://example.com/files/main/"),
819
819
  );
820
+
821
+ // Verify that popstateListener does not call applyRouteFromCurrentLocation when inFeedScroll is true
822
+ mockApplyRouteFromCurrentLocation.mockClear();
823
+ (win as any).history = { state: { inFeedScroll: true } };
824
+ shadowClaw.popstateListener();
825
+ expect(mockApplyRouteFromCurrentLocation).not.toHaveBeenCalled();
820
826
  });
821
827
  });
822
828
 
@@ -12,6 +12,7 @@ import { showPage } from "./showPage.js";
12
12
  import { supportsNavigationApi } from "./supportsNavigationApi.js";
13
13
  import { setupAppLifecycle } from "./setupAppLifecycle.js";
14
14
  import { syncPageHeaderMainVisibilityOverride } from "./syncPageHeaderMainVisibilityOverride.js";
15
+ import { setupSlottedSidebarActiveLinks } from "./syncSlottedSidebarActiveLinks.js";
15
16
 
16
17
  import { toggleActivityLogVisibility } from "./toggleActivityLogVisibility.js";
17
18
  import { togglePageHeaderMainVisibility } from "./togglePageHeaderMainVisibility.js";
@@ -88,6 +89,28 @@ export function bindEventListeners(
88
89
  });
89
90
  });
90
91
 
92
+ sidebar.addEventListener("click", (e: Event) => {
93
+ const target = e.target as HTMLElement | null;
94
+ if (
95
+ win.innerWidth < 896 &&
96
+ target?.closest("a, button, .nav-item, .sidebar-link, .sidebar-item")
97
+ ) {
98
+ sidebar.classList.remove("open");
99
+ }
100
+ });
101
+
102
+ if (typeof (shadowClaw as any)?.addEventListener === "function") {
103
+ shadowClaw.addEventListener("click", (e: Event) => {
104
+ const target = e.target as HTMLElement | null;
105
+ if (
106
+ win.innerWidth < 896 &&
107
+ target?.closest('[slot^="sidebar"] a, .sidebar-custom-sections a')
108
+ ) {
109
+ sidebar.classList.remove("open");
110
+ }
111
+ });
112
+ }
113
+
91
114
  // Close sidebar when clicking outside of it (mobile only).
92
115
  // Use composedPath() instead of e.target because Shadow DOM event
93
116
  // retargeting makes e.target the host element at the doc level,
@@ -251,7 +274,11 @@ export function bindEventListeners(
251
274
 
252
275
  shadowClaw.navigationListenerAttached = true;
253
276
  } else {
254
- shadowClaw.popstateListener = () => {
277
+ shadowClaw.popstateListener = (ev?: any) => {
278
+ const state = ev?.state || (win as any).history?.state;
279
+ if (state?.inFeedScroll || state?.scrollSpy || state?.suppressRouter) {
280
+ return;
281
+ }
255
282
  applyRouteFromCurrentLocation(
256
283
  shadow,
257
284
  shadowClaw,
@@ -312,4 +339,6 @@ export function bindEventListeners(
312
339
  shadowClaw.orchestrator,
313
340
  );
314
341
  }
342
+
343
+ setupSlottedSidebarActiveLinks(shadowClaw);
315
344
  }
@@ -93,4 +93,56 @@ describe("getRoute", () => {
93
93
 
94
94
  expect(result).toBeUndefined();
95
95
  });
96
+
97
+ it("returns undefined if destination state indicates an in-feed scroll", () => {
98
+ const navigateEvent = new Event("navigate") as any;
99
+ navigateEvent.navigationType = "replace";
100
+ navigateEvent.destination = {
101
+ url: `${window.location.origin}/test`,
102
+ getState: () => ({ inFeedScroll: true }),
103
+ };
104
+
105
+ const result = getRoute(db, navigateEvent);
106
+ expect(result).toBeUndefined();
107
+ expect(isPossibleAppRouteMock).not.toHaveBeenCalled();
108
+ });
109
+
110
+ it("returns undefined if window.history.state indicates an in-feed scroll", () => {
111
+ const origState = window.history.state;
112
+ try {
113
+ window.history.replaceState({ inFeedScroll: true }, "", "/test");
114
+ const navigateEvent = new Event("navigate") as any;
115
+ navigateEvent.navigationType = "replace";
116
+ navigateEvent.destination = {
117
+ url: `${window.location.origin}/test`,
118
+ getState: () => null,
119
+ };
120
+
121
+ const result = getRoute(db, navigateEvent);
122
+ expect(result).toBeUndefined();
123
+ expect(isPossibleAppRouteMock).not.toHaveBeenCalled();
124
+ } finally {
125
+ window.history.replaceState(origState, "", "/");
126
+ }
127
+ });
128
+
129
+ it("does not ignore push navigation even if window.history.state has inFeedScroll: true", () => {
130
+ const origState = window.history.state;
131
+ try {
132
+ window.history.replaceState({ inFeedScroll: true }, "", "/");
133
+ const navigateEvent = new Event("navigate") as any;
134
+ navigateEvent.navigationType = "push";
135
+ navigateEvent.destination = {
136
+ url: `${window.location.origin}/about`,
137
+ getState: () => null,
138
+ };
139
+
140
+ const result = getRoute(db, navigateEvent);
141
+ expect(result).toBeDefined();
142
+ expect(result?.parsedUrl.pathname).toBe("/about");
143
+ expect(isPossibleAppRouteMock).toHaveBeenCalledWith("/about");
144
+ } finally {
145
+ window.history.replaceState(origState, "", "/");
146
+ }
147
+ });
96
148
  });
@@ -11,6 +11,24 @@ export function getRoute(db: ShadowClawDatabase, ev: Event) {
11
11
  return;
12
12
  }
13
13
 
14
+ const destinationState =
15
+ typeof navigateEvent?.destination?.getState === "function"
16
+ ? navigateEvent.destination.getState()
17
+ : null;
18
+ const navState =
19
+ destinationState ||
20
+ (navigateEvent.navigationType === "replace" &&
21
+ typeof history !== "undefined"
22
+ ? history.state
23
+ : null);
24
+ if (
25
+ navState?.inFeedScroll ||
26
+ navState?.scrollSpy ||
27
+ navState?.suppressRouter
28
+ ) {
29
+ return;
30
+ }
31
+
14
32
  const destinationUrl = navigateEvent?.destination?.url;
15
33
  if (typeof destinationUrl !== "string") {
16
34
  return;
@@ -111,4 +111,25 @@ describe("navigateToRoute", () => {
111
111
  route,
112
112
  );
113
113
  });
114
+
115
+ it("should applyRoute directly without calling nav.navigate if already at finalPath", async () => {
116
+ mockSupportsNavigationApi.mockReturnValue(true);
117
+ const origPathname = window.location.pathname;
118
+ try {
119
+ window.history.replaceState({}, "", "/base/chat");
120
+ await navigateToRoute(shadowRoot, shadowClaw, db, fStore, oStore, route);
121
+
122
+ expect(mockNav.navigate).not.toHaveBeenCalled();
123
+ expect(mockApplyRoute).toHaveBeenCalledWith(
124
+ shadowRoot,
125
+ shadowClaw,
126
+ db,
127
+ fStore,
128
+ oStore,
129
+ route,
130
+ );
131
+ } finally {
132
+ window.history.replaceState({}, "", origPathname);
133
+ }
134
+ });
114
135
  });