diff --git a/.changeset/chilled-fishes-hunt.md b/.changeset/chilled-fishes-hunt.md
new file mode 100644
index 000000000..1584dab8e
--- /dev/null
+++ b/.changeset/chilled-fishes-hunt.md
@@ -0,0 +1,5 @@
+---
+"nostrudel": minor
+---
+
+Add tools menu under thread post
diff --git a/package.json b/package.json
index f1f147511..4a56b38e8 100644
--- a/package.json
+++ b/package.json
@@ -28,7 +28,7 @@
     "@codemirror/autocomplete": "^6.18.3",
     "@codemirror/lang-json": "^6.0.1",
     "@codemirror/language": "^6.10.3",
-    "@codemirror/view": "^6.34.2",
+    "@codemirror/view": "^6.34.3",
     "@emotion/react": "^11.13.3",
     "@emotion/styled": "^11.13.0",
     "@fedimint/core-web": "^0.0.7",
@@ -114,7 +114,7 @@
     "workbox-core": "7.0.0",
     "workbox-precaching": "7.0.0",
     "workbox-routing": "7.0.0",
-    "yet-another-react-lightbox": "^3.21.6",
+    "yet-another-react-lightbox": "^3.21.7",
     "zen-observable": "^0.10.0"
   },
   "devDependencies": {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 526f20d8d..7cbc76278 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -41,7 +41,7 @@ importers:
         version: 2.2.6(@chakra-ui/styled-system@2.12.0(react@18.3.1))(react@18.3.1)
       '@codemirror/autocomplete':
         specifier: ^6.18.3
-        version: 6.18.3(@codemirror/language@6.10.3)(@codemirror/state@6.4.1)(@codemirror/view@6.34.2)(@lezer/common@1.2.3)
+        version: 6.18.3(@codemirror/language@6.10.3)(@codemirror/state@6.4.1)(@codemirror/view@6.34.3)(@lezer/common@1.2.3)
       '@codemirror/lang-json':
         specifier: ^6.0.1
         version: 6.0.1
@@ -49,8 +49,8 @@ importers:
         specifier: ^6.10.3
         version: 6.10.3
       '@codemirror/view':
-        specifier: ^6.34.2
-        version: 6.34.2
+        specifier: ^6.34.3
+        version: 6.34.3
       '@emotion/react':
         specifier: ^11.13.3
         version: 11.13.3(@types/react@18.3.12)(react@18.3.1)
@@ -86,34 +86,34 @@ importers:
         version: 1.3.0
       '@uiw/codemirror-theme-github':
         specifier: ^4.23.6
-        version: 4.23.6(@codemirror/language@6.10.3)(@codemirror/state@6.4.1)(@codemirror/view@6.34.2)
+        version: 4.23.6(@codemirror/language@6.10.3)(@codemirror/state@6.4.1)(@codemirror/view@6.34.3)
       '@uiw/react-codemirror':
         specifier: ^4.23.6
-        version: 4.23.6(@babel/runtime@7.26.0)(@codemirror/autocomplete@6.18.3(@codemirror/language@6.10.3)(@codemirror/state@6.4.1)(@codemirror/view@6.34.2)(@lezer/common@1.2.3))(@codemirror/language@6.10.3)(@codemirror/lint@6.8.2)(@codemirror/search@6.5.6)(@codemirror/state@6.4.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.34.2)(codemirror@5.65.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        version: 4.23.6(@babel/runtime@7.26.0)(@codemirror/autocomplete@6.18.3(@codemirror/language@6.10.3)(@codemirror/state@6.4.1)(@codemirror/view@6.34.3)(@lezer/common@1.2.3))(@codemirror/language@6.10.3)(@codemirror/lint@6.8.2)(@codemirror/search@6.5.6)(@codemirror/state@6.4.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.34.3)(codemirror@5.65.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@webscopeio/react-textarea-autocomplete':
         specifier: ^4.9.2
         version: 4.9.2(prop-types@15.8.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       applesauce-channel:
         specifier: next
-        version: 0.0.0-next-20241114194041(typescript@5.6.3)
+        version: 0.0.0-next-20241115160057(typescript@5.6.3)
       applesauce-content:
         specifier: next
-        version: 0.0.0-next-20241114194041(typescript@5.6.3)
+        version: 0.0.0-next-20241115160057(typescript@5.6.3)
       applesauce-core:
         specifier: next
-        version: 0.0.0-next-20241114194041(typescript@5.6.3)
+        version: 0.0.0-next-20241115160057(typescript@5.6.3)
       applesauce-lists:
         specifier: next
-        version: 0.0.0-next-20241114194041(typescript@5.6.3)
+        version: 0.0.0-next-20241115160057(typescript@5.6.3)
       applesauce-net:
         specifier: next
-        version: 0.0.0-next-20241114194041(typescript@5.6.3)
+        version: 0.0.0-next-20241115160057(typescript@5.6.3)
       applesauce-react:
         specifier: next
-        version: 0.0.0-next-20241114194041(typescript@5.6.3)
+        version: 0.0.0-next-20241115160057(typescript@5.6.3)
       applesauce-signer:
         specifier: next
-        version: 0.0.0-next-20241114194041(typescript@5.6.3)
+        version: 0.0.0-next-20241115160057(typescript@5.6.3)
       bech32:
         specifier: ^2.0.0
         version: 2.0.0
@@ -137,7 +137,7 @@ importers:
         version: 2.6.0
       codemirror-json-schema:
         specifier: ^0.6.1
-        version: 0.6.1(@codemirror/language@6.10.3)(@codemirror/lint@6.8.2)(@codemirror/state@6.4.1)(@codemirror/view@6.34.2)(@lezer/common@1.2.3)
+        version: 0.6.1(@codemirror/language@6.10.3)(@codemirror/lint@6.8.2)(@codemirror/state@6.4.1)(@codemirror/view@6.34.3)(@lezer/common@1.2.3)
       dayjs:
         specifier: ^1.11.13
         version: 1.11.13
@@ -307,8 +307,8 @@ importers:
         specifier: 7.0.0
         version: 7.0.0
       yet-another-react-lightbox:
-        specifier: ^3.21.6
-        version: 3.21.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        specifier: ^3.21.7
+        version: 3.21.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       zen-observable:
         specifier: ^0.10.0
         version: 0.10.0
@@ -1130,8 +1130,8 @@ packages:
   '@codemirror/theme-one-dark@6.1.2':
     resolution: {integrity: sha512-F+sH0X16j/qFLMAfbciKTxVOwkdAS336b7AXTKOZhy8BR3eH/RelsnLgLFINrpST63mmN2OuwUt0W2ndUgYwUA==}
 
-  '@codemirror/view@6.34.2':
-    resolution: {integrity: sha512-d6n0WFvL970A9Z+l9N2dO+Hk9ev4hDYQzIx+B9tCyBP0W5wPEszi1rhuyFesNSkLZzXbQE5FPH7F/z/TMJfoPA==}
+  '@codemirror/view@6.34.3':
+    resolution: {integrity: sha512-Ph5d+u8DxIeSgssXEakaakImkzBV4+slwIbcxl9oc9evexJhImeu/G8TK7+zp+IFK9KuJ0BdSn6kTBJeH2CHvA==}
 
   '@emotion/babel-plugin@11.12.0':
     resolution: {integrity: sha512-y2WQb+oP8Jqvvclh8Q55gLUyb7UFvgv7eJfsj7td5TToBrIUtPay2kMrZi4xjq9qw2vD0ZR5fSho0yqoFgX7Rw==}
@@ -1516,93 +1516,93 @@ packages:
       rollup:
         optional: true
 
-  '@rollup/rollup-android-arm-eabi@4.26.0':
-    resolution: {integrity: sha512-gJNwtPDGEaOEgejbaseY6xMFu+CPltsc8/T+diUTTbOQLqD+bnrJq9ulH6WD69TqwqWmrfRAtUv30cCFZlbGTQ==}
+  '@rollup/rollup-android-arm-eabi@4.27.0':
+    resolution: {integrity: sha512-e312hTjuM89YLqlcqEs7mSvwhxN5pgXqRobUob7Jsz1wDQlpAb2WTX4jzvrx5NrL1h2SE4fGdHSNyPxbLfzyeA==}
     cpu: [arm]
     os: [android]
 
-  '@rollup/rollup-android-arm64@4.26.0':
-    resolution: {integrity: sha512-YJa5Gy8mEZgz5JquFruhJODMq3lTHWLm1fOy+HIANquLzfIOzE9RA5ie3JjCdVb9r46qfAQY/l947V0zfGJ0OQ==}
+  '@rollup/rollup-android-arm64@4.27.0':
+    resolution: {integrity: sha512-cBUOny8GNXP++gN00Bo5L04I2oqUEFAU0OSDb+4hqp4/R/pZL/zlGzp7lJkhtPX52Rj+PIe0S8aOqhK4hztxHQ==}
     cpu: [arm64]
     os: [android]
 
-  '@rollup/rollup-darwin-arm64@4.26.0':
-    resolution: {integrity: sha512-ErTASs8YKbqTBoPLp/kA1B1Um5YSom8QAc4rKhg7b9tyyVqDBlQxy7Bf2wW7yIlPGPg2UODDQcbkTlruPzDosw==}
+  '@rollup/rollup-darwin-arm64@4.27.0':
+    resolution: {integrity: sha512-aauK2M2ptFQQYdOqbKGYCg1LHlPbm6IxepSnHLLaMIGcd9YBiKRGl2+KtzQL/IkurP+b54EKBkvtZaWXijmzfQ==}
     cpu: [arm64]
     os: [darwin]
 
-  '@rollup/rollup-darwin-x64@4.26.0':
-    resolution: {integrity: sha512-wbgkYDHcdWW+NqP2mnf2NOuEbOLzDblalrOWcPyY6+BRbVhliavon15UploG7PpBRQ2bZJnbmh8o3yLoBvDIHA==}
+  '@rollup/rollup-darwin-x64@4.27.0':
+    resolution: {integrity: sha512-VAjOnHUwpvxf3XT33sMpsLGKq24Rz1sTQhLuUicYrV9pxB4TNi0w11qAGPOyR+dQu/iZf88DmEmG0+2Gaqa1gg==}
     cpu: [x64]
     os: [darwin]
 
-  '@rollup/rollup-freebsd-arm64@4.26.0':
-    resolution: {integrity: sha512-Y9vpjfp9CDkAG4q/uwuhZk96LP11fBz/bYdyg9oaHYhtGZp7NrbkQrj/66DYMMP2Yo/QPAsVHkV891KyO52fhg==}
+  '@rollup/rollup-freebsd-arm64@4.27.0':
+    resolution: {integrity: sha512-I2eRlZG87gl6WxP6PvSB5bfFA1btE7tWnG6QAoEU/0Gr47f6KaxRwiRfBujHlzkuMPqtpTlSOW4aOEOyMtQqfg==}
     cpu: [arm64]
     os: [freebsd]
 
-  '@rollup/rollup-freebsd-x64@4.26.0':
-    resolution: {integrity: sha512-A/jvfCZ55EYPsqeaAt/yDAG4q5tt1ZboWMHEvKAH9Zl92DWvMIbnZe/f/eOXze65aJaaKbL+YeM0Hz4kLQvdwg==}
+  '@rollup/rollup-freebsd-x64@4.27.0':
+    resolution: {integrity: sha512-G05JNYFdjikD/2hJTf1gHdD5KjI2TotjiDn17amHtB5JgwrRF1EA9hJ3TRGIvT3zGXilNWWlR71R/2TT1pXRDg==}
     cpu: [x64]
     os: [freebsd]
 
-  '@rollup/rollup-linux-arm-gnueabihf@4.26.0':
-    resolution: {integrity: sha512-paHF1bMXKDuizaMODm2bBTjRiHxESWiIyIdMugKeLnjuS1TCS54MF5+Y5Dx8Ui/1RBPVRE09i5OUlaLnv8OGnA==}
+  '@rollup/rollup-linux-arm-gnueabihf@4.27.0':
+    resolution: {integrity: sha512-FMXxMZ7qnMULwgdmGSFVlOduAhFyqDPoK1A2Q8HBkzGYX9SMFU3ITKfLdIiCzTaaj/pt1OiEbpF2szUw6Kh++Q==}
     cpu: [arm]
     os: [linux]
 
-  '@rollup/rollup-linux-arm-musleabihf@4.26.0':
-    resolution: {integrity: sha512-cwxiHZU1GAs+TMxvgPfUDtVZjdBdTsQwVnNlzRXC5QzIJ6nhfB4I1ahKoe9yPmoaA/Vhf7m9dB1chGPpDRdGXg==}
+  '@rollup/rollup-linux-arm-musleabihf@4.27.0':
+    resolution: {integrity: sha512-0315TiPsJfOY+jAwEeqxcy9yVcAy/jg99GrMcd/L7CRESzi1vhyLPbnkDnz7giaEttSRf/d3llJYfoC+44Nl3A==}
     cpu: [arm]
     os: [linux]
 
-  '@rollup/rollup-linux-arm64-gnu@4.26.0':
-    resolution: {integrity: sha512-4daeEUQutGRCW/9zEo8JtdAgtJ1q2g5oHaoQaZbMSKaIWKDQwQ3Yx0/3jJNmpzrsScIPtx/V+1AfibLisb3AMQ==}
+  '@rollup/rollup-linux-arm64-gnu@4.27.0':
+    resolution: {integrity: sha512-4zCKY5E9djPyHzvoCWIouFsuAvg+dk+rNia8lz1bjKpzKz02QvK4JPHyjcDT8CFR2J/aA98WccCirdDOy+VDWQ==}
     cpu: [arm64]
     os: [linux]
 
-  '@rollup/rollup-linux-arm64-musl@4.26.0':
-    resolution: {integrity: sha512-eGkX7zzkNxvvS05ROzJ/cO/AKqNvR/7t1jA3VZDi2vRniLKwAWxUr85fH3NsvtxU5vnUUKFHKh8flIBdlo2b3Q==}
+  '@rollup/rollup-linux-arm64-musl@4.27.0':
+    resolution: {integrity: sha512-6St9rrPSLbYBbbJAClpU4gmnO7cdZCMMzx2MT0UCIIIevoLAmsCDOAG6t3J/RgN4CPUpdaGr/UnPqQTHZ4oDwA==}
     cpu: [arm64]
     os: [linux]
 
-  '@rollup/rollup-linux-powerpc64le-gnu@4.26.0':
-    resolution: {integrity: sha512-Odp/lgHbW/mAqw/pU21goo5ruWsytP7/HCC/liOt0zcGG0llYWKrd10k9Fj0pdj3prQ63N5yQLCLiE7HTX+MYw==}
+  '@rollup/rollup-linux-powerpc64le-gnu@4.27.0':
+    resolution: {integrity: sha512-dIBfp8NDrgvwUJxyqFv7501coIba+7xxBJy1gQEF0RGkIKa3Tq0Mh3sF9hmstDLtaMt7gL2aXsCNG9SCKyVVZg==}
     cpu: [ppc64]
     os: [linux]
 
-  '@rollup/rollup-linux-riscv64-gnu@4.26.0':
-    resolution: {integrity: sha512-MBR2ZhCTzUgVD0OJdTzNeF4+zsVogIR1U/FsyuFerwcqjZGvg2nYe24SAHp8O5sN8ZkRVbHwlYeHqcSQ8tcYew==}
+  '@rollup/rollup-linux-riscv64-gnu@4.27.0':
+    resolution: {integrity: sha512-Pu7xLHRy+5UjFCKR/vWsbFmiBYUC9993v99YoKWhAgK4VsdNuWHPs17NuCJEtVsZpYCNVPbRyBpQw58Ma8BmeA==}
     cpu: [riscv64]
     os: [linux]
 
-  '@rollup/rollup-linux-s390x-gnu@4.26.0':
-    resolution: {integrity: sha512-YYcg8MkbN17fMbRMZuxwmxWqsmQufh3ZJFxFGoHjrE7bv0X+T6l3glcdzd7IKLiwhT+PZOJCblpnNlz1/C3kGQ==}
+  '@rollup/rollup-linux-s390x-gnu@4.27.0':
+    resolution: {integrity: sha512-2Q9qQnk/eWdvXzzHl22y7tpDHREppFUh7N6cCs70HZEbQSgB7wd/2S/B05SSiyAiIn5BL+fYiASLds5bz0IQFw==}
     cpu: [s390x]
     os: [linux]
 
-  '@rollup/rollup-linux-x64-gnu@4.26.0':
-    resolution: {integrity: sha512-ZuwpfjCwjPkAOxpjAEjabg6LRSfL7cAJb6gSQGZYjGhadlzKKywDkCUnJ+KEfrNY1jH5EEoSIKLCb572jSiglA==}
+  '@rollup/rollup-linux-x64-gnu@4.27.0':
+    resolution: {integrity: sha512-CNnqMZ4Yz0Ga0A75qux7DNChq0P9oAWn2S7yjZPRC+AaEF8Ysw5K/1lzT25/a3reJ4V2abcShIVG+tfZHb1UrQ==}
     cpu: [x64]
     os: [linux]
 
-  '@rollup/rollup-linux-x64-musl@4.26.0':
-    resolution: {integrity: sha512-+HJD2lFS86qkeF8kNu0kALtifMpPCZU80HvwztIKnYwym3KnA1os6nsX4BGSTLtS2QVAGG1P3guRgsYyMA0Yhg==}
+  '@rollup/rollup-linux-x64-musl@4.27.0':
+    resolution: {integrity: sha512-dS1+eCbbao54XB+wLW6uuwRkChq4L0UfKhd3wvt6s+EN1rTIi24ee5Lk3HfRGq9J2jsRm12/AGKLA0kd82Sp/g==}
     cpu: [x64]
     os: [linux]
 
-  '@rollup/rollup-win32-arm64-msvc@4.26.0':
-    resolution: {integrity: sha512-WUQzVFWPSw2uJzX4j6YEbMAiLbs0BUysgysh8s817doAYhR5ybqTI1wtKARQKo6cGop3pHnrUJPFCsXdoFaimQ==}
+  '@rollup/rollup-win32-arm64-msvc@4.27.0':
+    resolution: {integrity: sha512-VrYQHY5+Y71OU/uOSRE9lLhph16bbuWGrMwGwZDPxCUXUW5NgLA+K+q0kv7rafHRlnrsZSVcMOkZskzTNnR3ZQ==}
     cpu: [arm64]
     os: [win32]
 
-  '@rollup/rollup-win32-ia32-msvc@4.26.0':
-    resolution: {integrity: sha512-D4CxkazFKBfN1akAIY6ieyOqzoOoBV1OICxgUblWxff/pSjCA2khXlASUx7mK6W1oP4McqhgcCsu6QaLj3WMWg==}
+  '@rollup/rollup-win32-ia32-msvc@4.27.0':
+    resolution: {integrity: sha512-LCqk4Xi3e4GzLqaq+QDM7gP5DtJ/RgWMzV3U2brwp/vEz9RTA5YBgIDP69xYfrTXexes6xPsOIquy79+kLifiA==}
     cpu: [ia32]
     os: [win32]
 
-  '@rollup/rollup-win32-x64-msvc@4.26.0':
-    resolution: {integrity: sha512-2x8MO1rm4PGEP0xWbubJW5RtbNLk3puzAMaLQd3B3JHVw4KcHlmXcO+Wewx9zCoo7EUFiMlu/aZbCJ7VjMzAag==}
+  '@rollup/rollup-win32-x64-msvc@4.27.0':
+    resolution: {integrity: sha512-dj2ZolfViR3chLWwSHID2mBzLLwYvXFldIplR6BSkdACXqAsrcmItKTff4h7enYB3Ugoh0v41WbxijE9HJb1Hw==}
     cpu: [x64]
     os: [win32]
 
@@ -1878,26 +1878,26 @@ packages:
     resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
     engines: {node: '>=8'}
 
-  applesauce-channel@0.0.0-next-20241114194041:
-    resolution: {integrity: sha512-8ItnMP36x33EdgKPJ6EDn0I2ETCW5EfbrvNpQUfh8wP+ppBVQFuhaV3UmLVRRbv+Mq4RL1Jo60HLqul60O0kYw==}
+  applesauce-channel@0.0.0-next-20241115160057:
+    resolution: {integrity: sha512-0/F0Uw+n/j51n3NIx/q1W94jkycfDffuKRFFIHdaFDC3dH2v/UnQX1LC8lOrVPoNfx6DqGK0sZZ3R6fk4OnKEQ==}
 
-  applesauce-content@0.0.0-next-20241114194041:
-    resolution: {integrity: sha512-0nOMZN8Jdcge4lxeuWxLIfMHfbfwdUFKEJRIxo4nb9nbms5Tuhjz9TQhvlQ/PN+0f0ioIy0nFjw7xTyJV/6sMg==}
+  applesauce-content@0.0.0-next-20241115160057:
+    resolution: {integrity: sha512-fJZFbiln+drZxEp82xJbyR3GBTysbswdtiliDN+vVSt66pbVC7QXF+7yp8CskrZm7+5BnuP59zT4Q18cipcjYw==}
 
-  applesauce-core@0.0.0-next-20241114194041:
-    resolution: {integrity: sha512-vPeqWHLr0DklswpmCDEMKaZ4yVwaM+8lxX7Yix8cCZvZzaXv/yxg3AlxbUcwjIVuVscuQp/zO1+soB27Coh4fQ==}
+  applesauce-core@0.0.0-next-20241115160057:
+    resolution: {integrity: sha512-9zvY/NXeJfgYk4cMaELoKVckBQnEDfobhwvxBzCkrQfh6K87mMPDbAemaaW8zp9Ggm1ejbGWpDqnBEnj+d6zvw==}
 
-  applesauce-lists@0.0.0-next-20241114194041:
-    resolution: {integrity: sha512-OGNQxwJ7rdunWkNaUxKkOmm0ioIAaOVEVUpKi6KWHKob4Z+I8B2onWMz/ROtNnsUafitIGK6Vp5pl3EJpzGsuw==}
+  applesauce-lists@0.0.0-next-20241115160057:
+    resolution: {integrity: sha512-2nuPegvbDyOuJB8szf/MSi80/yQhxVGcgoQ9MciNhDj7LgxdK/HABo/oaPmtBqgUEYkixO8Pe9wYnci//ooKIQ==}
 
-  applesauce-net@0.0.0-next-20241114194041:
-    resolution: {integrity: sha512-WtaXP59JGzCE2TJlJQ25AudcgI5rp6jI5Y7cxtYey+vxdcu931MMvod9Uo6ppgvP/U5voJusWrxEXxUWKbUIZw==}
+  applesauce-net@0.0.0-next-20241115160057:
+    resolution: {integrity: sha512-FvYHh2u7cYpIixUTN5sG18zTHj5PKWR/tcik9PCIfRPgte25zcYr1bTXYPitJnzwl5DLA85acYPjSuJlTc9vtg==}
 
-  applesauce-react@0.0.0-next-20241114194041:
-    resolution: {integrity: sha512-ARJjt60kjXPZk8Jeoa0xOgu5c7j66kmzqdBs+eFW7246oVxzUHyy2JpwewPNASDCjHNhaOnUVmspU2gBTF4svA==}
+  applesauce-react@0.0.0-next-20241115160057:
+    resolution: {integrity: sha512-OtBBHVdOTVOIzfzRQVgZz7kQsBfph4LPoSJ53Pj46JzEgiXyHoQDxdJ3j1h9apQsHH6hGNnTtfrH5W/U0EAwog==}
 
-  applesauce-signer@0.0.0-next-20241114194041:
-    resolution: {integrity: sha512-cxZVB3dcTwMq4XzOCOZheMlKB0My//x278fR/JCfaYiL9dghMCZCWwU4vUOXgyMQ4cW3csD+8RXNo0ICAG8Fpw==}
+  applesauce-signer@0.0.0-next-20241115160057:
+    resolution: {integrity: sha512-Iw9v4ylqzxyISrylZawZM+y+tWhvHRBXKmYpxKwuSkBtX9mWEUnItSKcC9M1g2ncYvima2JrOvgPr3oGEZIaKA==}
 
   argparse@1.0.10:
     resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
@@ -2361,8 +2361,8 @@ packages:
     engines: {node: '>=0.10.0'}
     hasBin: true
 
-  electron-to-chromium@1.5.58:
-    resolution: {integrity: sha512-al2l4r+24ZFL7WzyPTlyD0fC33LLzvxqLCwurtBibVPghRGO9hSTl+tis8t1kD7biPiH/en4U0I7o/nQbYeoVA==}
+  electron-to-chromium@1.5.60:
+    resolution: {integrity: sha512-HcraRUkTKJ+8yA3b10i9qvhUlPBRDlKjn1XGek1zDGVfAKcvi8TsUnImGqLiEm9j6ZulxXIWWIo9BmbkbCTGgA==}
 
   emoji-regex@10.4.0:
     resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==}
@@ -2387,8 +2387,8 @@ packages:
   error-stack-parser@2.1.4:
     resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==}
 
-  es-abstract@1.23.4:
-    resolution: {integrity: sha512-HR1gxH5OaiN7XH7uiWH0RLw0RcFySiSoW1ctxmD1ahTw3uGBtkmm/ng0tDU1OtYx5OK6EOL5Y6O21cDflG3Jcg==}
+  es-abstract@1.23.5:
+    resolution: {integrity: sha512-vlmniQ0WNPwXqA0BnmwV3Ng7HxiGlh6r5U6JcTMNx8OilcAGqVJBHJcPjqOMaczU9fRuRK5Px2BdVyPRnKMMVQ==}
     engines: {node: '>= 0.4'}
 
   es-define-property@1.0.0:
@@ -3738,8 +3738,8 @@ packages:
     engines: {node: '>=10.0.0'}
     hasBin: true
 
-  rollup@4.26.0:
-    resolution: {integrity: sha512-ilcl12hnWonG8f+NxU6BlgysVA0gvY2l8N0R84S1HcINbW20bvwuCngJkkInV6LXhwRpucsW5k1ovDwEdBVrNg==}
+  rollup@4.27.0:
+    resolution: {integrity: sha512-nrOD/RrnAMssruS7bPa7MYpEuH6tUpOa43NLtxQiLKem0An8HZyXun5Ndig6JzbkJoIbaKkt85V67VCaQ59GyA==}
     engines: {node: '>=18.0.0', npm: '>=8.0.0'}
     hasBin: true
 
@@ -4318,8 +4318,8 @@ packages:
     resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==}
     engines: {node: '>= 6'}
 
-  yet-another-react-lightbox@3.21.6:
-    resolution: {integrity: sha512-uKcRmmezsj1Fbj38B6hFOGwbAu94fPr8d5H6I0+1FmcToX56freEGXXXtdA1oRo6036ug+UgrKZzzvsw/MIM/w==}
+  yet-another-react-lightbox@3.21.7:
+    resolution: {integrity: sha512-dcdokNuCIl92f0Vl+uzeKULnQhztIGpoZFUMvtVNUPmtwsQWpqWufeieDPeg9JtFyVCcbj4vYw3V00DS0QNoWA==}
     engines: {node: '>=14'}
     peerDependencies:
       react: '>=16.8.0'
@@ -5395,18 +5395,18 @@ snapshots:
       human-id: 1.0.2
       prettier: 2.8.8
 
-  '@codemirror/autocomplete@6.18.3(@codemirror/language@6.10.3)(@codemirror/state@6.4.1)(@codemirror/view@6.34.2)(@lezer/common@1.2.3)':
+  '@codemirror/autocomplete@6.18.3(@codemirror/language@6.10.3)(@codemirror/state@6.4.1)(@codemirror/view@6.34.3)(@lezer/common@1.2.3)':
     dependencies:
       '@codemirror/language': 6.10.3
       '@codemirror/state': 6.4.1
-      '@codemirror/view': 6.34.2
+      '@codemirror/view': 6.34.3
       '@lezer/common': 1.2.3
 
   '@codemirror/commands@6.7.1':
     dependencies:
       '@codemirror/language': 6.10.3
       '@codemirror/state': 6.4.1
-      '@codemirror/view': 6.34.2
+      '@codemirror/view': 6.34.3
       '@lezer/common': 1.2.3
 
   '@codemirror/lang-json@6.0.1':
@@ -5417,7 +5417,7 @@ snapshots:
   '@codemirror/language@6.10.3':
     dependencies:
       '@codemirror/state': 6.4.1
-      '@codemirror/view': 6.34.2
+      '@codemirror/view': 6.34.3
       '@lezer/common': 1.2.3
       '@lezer/highlight': 1.2.1
       '@lezer/lr': 1.4.2
@@ -5426,13 +5426,13 @@ snapshots:
   '@codemirror/lint@6.8.2':
     dependencies:
       '@codemirror/state': 6.4.1
-      '@codemirror/view': 6.34.2
+      '@codemirror/view': 6.34.3
       crelt: 1.0.6
 
   '@codemirror/search@6.5.6':
     dependencies:
       '@codemirror/state': 6.4.1
-      '@codemirror/view': 6.34.2
+      '@codemirror/view': 6.34.3
       crelt: 1.0.6
 
   '@codemirror/state@6.4.1': {}
@@ -5441,10 +5441,10 @@ snapshots:
     dependencies:
       '@codemirror/language': 6.10.3
       '@codemirror/state': 6.4.1
-      '@codemirror/view': 6.34.2
+      '@codemirror/view': 6.34.3
       '@lezer/highlight': 1.2.1
 
-  '@codemirror/view@6.34.2':
+  '@codemirror/view@6.34.3':
     dependencies:
       '@codemirror/state': 6.4.1
       style-mod: 4.1.2
@@ -5818,58 +5818,58 @@ snapshots:
     optionalDependencies:
       rollup: 2.79.2
 
-  '@rollup/rollup-android-arm-eabi@4.26.0':
+  '@rollup/rollup-android-arm-eabi@4.27.0':
     optional: true
 
-  '@rollup/rollup-android-arm64@4.26.0':
+  '@rollup/rollup-android-arm64@4.27.0':
     optional: true
 
-  '@rollup/rollup-darwin-arm64@4.26.0':
+  '@rollup/rollup-darwin-arm64@4.27.0':
     optional: true
 
-  '@rollup/rollup-darwin-x64@4.26.0':
+  '@rollup/rollup-darwin-x64@4.27.0':
     optional: true
 
-  '@rollup/rollup-freebsd-arm64@4.26.0':
+  '@rollup/rollup-freebsd-arm64@4.27.0':
     optional: true
 
-  '@rollup/rollup-freebsd-x64@4.26.0':
+  '@rollup/rollup-freebsd-x64@4.27.0':
     optional: true
 
-  '@rollup/rollup-linux-arm-gnueabihf@4.26.0':
+  '@rollup/rollup-linux-arm-gnueabihf@4.27.0':
     optional: true
 
-  '@rollup/rollup-linux-arm-musleabihf@4.26.0':
+  '@rollup/rollup-linux-arm-musleabihf@4.27.0':
     optional: true
 
-  '@rollup/rollup-linux-arm64-gnu@4.26.0':
+  '@rollup/rollup-linux-arm64-gnu@4.27.0':
     optional: true
 
-  '@rollup/rollup-linux-arm64-musl@4.26.0':
+  '@rollup/rollup-linux-arm64-musl@4.27.0':
     optional: true
 
-  '@rollup/rollup-linux-powerpc64le-gnu@4.26.0':
+  '@rollup/rollup-linux-powerpc64le-gnu@4.27.0':
     optional: true
 
-  '@rollup/rollup-linux-riscv64-gnu@4.26.0':
+  '@rollup/rollup-linux-riscv64-gnu@4.27.0':
     optional: true
 
-  '@rollup/rollup-linux-s390x-gnu@4.26.0':
+  '@rollup/rollup-linux-s390x-gnu@4.27.0':
     optional: true
 
-  '@rollup/rollup-linux-x64-gnu@4.26.0':
+  '@rollup/rollup-linux-x64-gnu@4.27.0':
     optional: true
 
-  '@rollup/rollup-linux-x64-musl@4.26.0':
+  '@rollup/rollup-linux-x64-musl@4.27.0':
     optional: true
 
-  '@rollup/rollup-win32-arm64-msvc@4.26.0':
+  '@rollup/rollup-win32-arm64-msvc@4.27.0':
     optional: true
 
-  '@rollup/rollup-win32-ia32-msvc@4.26.0':
+  '@rollup/rollup-win32-ia32-msvc@4.27.0':
     optional: true
 
-  '@rollup/rollup-win32-x64-msvc@4.26.0':
+  '@rollup/rollup-win32-x64-msvc@4.27.0':
     optional: true
 
   '@sagold/json-pointer@5.1.2': {}
@@ -6073,38 +6073,38 @@ snapshots:
 
   '@types/zen-observable@0.8.7': {}
 
-  '@uiw/codemirror-extensions-basic-setup@4.23.6(@codemirror/autocomplete@6.18.3(@codemirror/language@6.10.3)(@codemirror/state@6.4.1)(@codemirror/view@6.34.2)(@lezer/common@1.2.3))(@codemirror/commands@6.7.1)(@codemirror/language@6.10.3)(@codemirror/lint@6.8.2)(@codemirror/search@6.5.6)(@codemirror/state@6.4.1)(@codemirror/view@6.34.2)':
+  '@uiw/codemirror-extensions-basic-setup@4.23.6(@codemirror/autocomplete@6.18.3(@codemirror/language@6.10.3)(@codemirror/state@6.4.1)(@codemirror/view@6.34.3)(@lezer/common@1.2.3))(@codemirror/commands@6.7.1)(@codemirror/language@6.10.3)(@codemirror/lint@6.8.2)(@codemirror/search@6.5.6)(@codemirror/state@6.4.1)(@codemirror/view@6.34.3)':
     dependencies:
-      '@codemirror/autocomplete': 6.18.3(@codemirror/language@6.10.3)(@codemirror/state@6.4.1)(@codemirror/view@6.34.2)(@lezer/common@1.2.3)
+      '@codemirror/autocomplete': 6.18.3(@codemirror/language@6.10.3)(@codemirror/state@6.4.1)(@codemirror/view@6.34.3)(@lezer/common@1.2.3)
       '@codemirror/commands': 6.7.1
       '@codemirror/language': 6.10.3
       '@codemirror/lint': 6.8.2
       '@codemirror/search': 6.5.6
       '@codemirror/state': 6.4.1
-      '@codemirror/view': 6.34.2
+      '@codemirror/view': 6.34.3
 
-  '@uiw/codemirror-theme-github@4.23.6(@codemirror/language@6.10.3)(@codemirror/state@6.4.1)(@codemirror/view@6.34.2)':
+  '@uiw/codemirror-theme-github@4.23.6(@codemirror/language@6.10.3)(@codemirror/state@6.4.1)(@codemirror/view@6.34.3)':
     dependencies:
-      '@uiw/codemirror-themes': 4.23.6(@codemirror/language@6.10.3)(@codemirror/state@6.4.1)(@codemirror/view@6.34.2)
+      '@uiw/codemirror-themes': 4.23.6(@codemirror/language@6.10.3)(@codemirror/state@6.4.1)(@codemirror/view@6.34.3)
     transitivePeerDependencies:
       - '@codemirror/language'
       - '@codemirror/state'
       - '@codemirror/view'
 
-  '@uiw/codemirror-themes@4.23.6(@codemirror/language@6.10.3)(@codemirror/state@6.4.1)(@codemirror/view@6.34.2)':
+  '@uiw/codemirror-themes@4.23.6(@codemirror/language@6.10.3)(@codemirror/state@6.4.1)(@codemirror/view@6.34.3)':
     dependencies:
       '@codemirror/language': 6.10.3
       '@codemirror/state': 6.4.1
-      '@codemirror/view': 6.34.2
+      '@codemirror/view': 6.34.3
 
-  '@uiw/react-codemirror@4.23.6(@babel/runtime@7.26.0)(@codemirror/autocomplete@6.18.3(@codemirror/language@6.10.3)(@codemirror/state@6.4.1)(@codemirror/view@6.34.2)(@lezer/common@1.2.3))(@codemirror/language@6.10.3)(@codemirror/lint@6.8.2)(@codemirror/search@6.5.6)(@codemirror/state@6.4.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.34.2)(codemirror@5.65.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+  '@uiw/react-codemirror@4.23.6(@babel/runtime@7.26.0)(@codemirror/autocomplete@6.18.3(@codemirror/language@6.10.3)(@codemirror/state@6.4.1)(@codemirror/view@6.34.3)(@lezer/common@1.2.3))(@codemirror/language@6.10.3)(@codemirror/lint@6.8.2)(@codemirror/search@6.5.6)(@codemirror/state@6.4.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.34.3)(codemirror@5.65.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
     dependencies:
       '@babel/runtime': 7.26.0
       '@codemirror/commands': 6.7.1
       '@codemirror/state': 6.4.1
       '@codemirror/theme-one-dark': 6.1.2
-      '@codemirror/view': 6.34.2
-      '@uiw/codemirror-extensions-basic-setup': 4.23.6(@codemirror/autocomplete@6.18.3(@codemirror/language@6.10.3)(@codemirror/state@6.4.1)(@codemirror/view@6.34.2)(@lezer/common@1.2.3))(@codemirror/commands@6.7.1)(@codemirror/language@6.10.3)(@codemirror/lint@6.8.2)(@codemirror/search@6.5.6)(@codemirror/state@6.4.1)(@codemirror/view@6.34.2)
+      '@codemirror/view': 6.34.3
+      '@uiw/codemirror-extensions-basic-setup': 4.23.6(@codemirror/autocomplete@6.18.3(@codemirror/language@6.10.3)(@codemirror/state@6.4.1)(@codemirror/view@6.34.3)(@lezer/common@1.2.3))(@codemirror/commands@6.7.1)(@codemirror/language@6.10.3)(@codemirror/lint@6.8.2)(@codemirror/search@6.5.6)(@codemirror/state@6.4.1)(@codemirror/view@6.34.3)
       codemirror: 5.65.18
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
@@ -6173,22 +6173,22 @@ snapshots:
     dependencies:
       color-convert: 2.0.1
 
-  applesauce-channel@0.0.0-next-20241114194041(typescript@5.6.3):
+  applesauce-channel@0.0.0-next-20241115160057(typescript@5.6.3):
     dependencies:
-      applesauce-core: 0.0.0-next-20241114194041(typescript@5.6.3)
+      applesauce-core: 0.0.0-next-20241115160057(typescript@5.6.3)
       nostr-tools: 2.10.3(typescript@5.6.3)
       rxjs: 7.8.1
     transitivePeerDependencies:
       - supports-color
       - typescript
 
-  applesauce-content@0.0.0-next-20241114194041(typescript@5.6.3):
+  applesauce-content@0.0.0-next-20241115160057(typescript@5.6.3):
     dependencies:
       '@cashu/cashu-ts': 2.0.0-rc1
       '@types/hast': 3.0.4
       '@types/mdast': 4.0.4
       '@types/unist': 3.0.3
-      applesauce-core: 0.0.0-next-20241114194041(typescript@5.6.3)
+      applesauce-core: 0.0.0-next-20241115160057(typescript@5.6.3)
       mdast-util-find-and-replace: 3.0.1
       nostr-tools: 2.10.3(typescript@5.6.3)
       remark: 15.0.1
@@ -6199,7 +6199,7 @@ snapshots:
       - supports-color
       - typescript
 
-  applesauce-core@0.0.0-next-20241114194041(typescript@5.6.3):
+  applesauce-core@0.0.0-next-20241115160057(typescript@5.6.3):
     dependencies:
       debug: 4.3.7
       json-stringify-deterministic: 1.0.12
@@ -6211,13 +6211,13 @@ snapshots:
       - supports-color
       - typescript
 
-  applesauce-lists@0.0.0-next-20241114194041(typescript@5.6.3):
+  applesauce-lists@0.0.0-next-20241115160057(typescript@5.6.3):
     dependencies:
       '@noble/hashes': 1.5.0
       '@noble/secp256k1': 1.7.1
       '@scure/base': 1.1.9
       '@types/dom-serial': 1.0.6
-      applesauce-core: 0.0.0-next-20241114194041(typescript@5.6.3)
+      applesauce-core: 0.0.0-next-20241115160057(typescript@5.6.3)
       debug: 4.3.7
       nostr-tools: 2.10.3(typescript@5.6.3)
       rxjs: 7.8.1
@@ -6225,9 +6225,9 @@ snapshots:
       - supports-color
       - typescript
 
-  applesauce-net@0.0.0-next-20241114194041(typescript@5.6.3):
+  applesauce-net@0.0.0-next-20241115160057(typescript@5.6.3):
     dependencies:
-      applesauce-core: 0.0.0-next-20241114194041(typescript@5.6.3)
+      applesauce-core: 0.0.0-next-20241115160057(typescript@5.6.3)
       nanoid: 5.0.8
       nostr-tools: 2.10.3(typescript@5.6.3)
       rxjs: 7.8.1
@@ -6236,10 +6236,10 @@ snapshots:
       - supports-color
       - typescript
 
-  applesauce-react@0.0.0-next-20241114194041(typescript@5.6.3):
+  applesauce-react@0.0.0-next-20241115160057(typescript@5.6.3):
     dependencies:
-      applesauce-content: 0.0.0-next-20241114194041(typescript@5.6.3)
-      applesauce-core: 0.0.0-next-20241114194041(typescript@5.6.3)
+      applesauce-content: 0.0.0-next-20241115160057(typescript@5.6.3)
+      applesauce-core: 0.0.0-next-20241115160057(typescript@5.6.3)
       nostr-tools: 2.10.3(typescript@5.6.3)
       react: 18.3.1
       rxjs: 7.8.1
@@ -6247,14 +6247,14 @@ snapshots:
       - supports-color
       - typescript
 
-  applesauce-signer@0.0.0-next-20241114194041(typescript@5.6.3):
+  applesauce-signer@0.0.0-next-20241115160057(typescript@5.6.3):
     dependencies:
       '@noble/hashes': 1.5.0
       '@noble/secp256k1': 1.7.1
       '@scure/base': 1.1.9
       '@types/dom-serial': 1.0.6
-      applesauce-core: 0.0.0-next-20241114194041(typescript@5.6.3)
-      applesauce-net: 0.0.0-next-20241114194041(typescript@5.6.3)
+      applesauce-core: 0.0.0-next-20241115160057(typescript@5.6.3)
+      applesauce-net: 0.0.0-next-20241115160057(typescript@5.6.3)
       debug: 4.3.7
       nanoid: 5.0.8
       nostr-tools: 2.10.3(typescript@5.6.3)
@@ -6282,7 +6282,7 @@ snapshots:
       array-buffer-byte-length: 1.0.1
       call-bind: 1.0.7
       define-properties: 1.2.1
-      es-abstract: 1.23.4
+      es-abstract: 1.23.5
       es-errors: 1.3.0
       get-intrinsic: 1.2.4
       is-array-buffer: 3.0.4
@@ -6385,7 +6385,7 @@ snapshots:
   browserslist@4.24.2:
     dependencies:
       caniuse-lite: 1.0.30001680
-      electron-to-chromium: 1.5.58
+      electron-to-chromium: 1.5.60
       node-releases: 2.0.18
       update-browserslist-db: 1.1.1(browserslist@4.24.2)
 
@@ -6470,13 +6470,13 @@ snapshots:
 
   classnames@2.5.1: {}
 
-  codemirror-json-schema@0.6.1(@codemirror/language@6.10.3)(@codemirror/lint@6.8.2)(@codemirror/state@6.4.1)(@codemirror/view@6.34.2)(@lezer/common@1.2.3):
+  codemirror-json-schema@0.6.1(@codemirror/language@6.10.3)(@codemirror/lint@6.8.2)(@codemirror/state@6.4.1)(@codemirror/view@6.34.3)(@lezer/common@1.2.3):
     dependencies:
       '@changesets/changelog-github': 0.4.8
       '@codemirror/language': 6.10.3
       '@codemirror/lint': 6.8.2
       '@codemirror/state': 6.4.1
-      '@codemirror/view': 6.34.2
+      '@codemirror/view': 6.34.3
       '@lezer/common': 1.2.3
       '@sagold/json-pointer': 5.1.2
       '@types/json-schema': 7.0.15
@@ -6494,7 +6494,7 @@ snapshots:
     dependencies:
       '@codemirror/language': 6.10.3
       '@codemirror/state': 6.4.1
-      '@codemirror/view': 6.34.2
+      '@codemirror/view': 6.34.3
       '@lezer/common': 1.2.3
       '@lezer/highlight': 1.2.1
       json5: 2.2.3
@@ -6779,7 +6779,7 @@ snapshots:
     dependencies:
       jake: 10.9.2
 
-  electron-to-chromium@1.5.58: {}
+  electron-to-chromium@1.5.60: {}
 
   emoji-regex@10.4.0: {}
 
@@ -6805,7 +6805,7 @@ snapshots:
     dependencies:
       stackframe: 1.3.4
 
-  es-abstract@1.23.4:
+  es-abstract@1.23.5:
     dependencies:
       array-buffer-byte-length: 1.0.1
       arraybuffer.prototype.slice: 1.0.3
@@ -7040,7 +7040,7 @@ snapshots:
     dependencies:
       call-bind: 1.0.7
       define-properties: 1.2.1
-      es-abstract: 1.23.4
+      es-abstract: 1.23.5
       functions-have-names: 1.2.3
 
   functions-have-names@1.2.3: {}
@@ -8480,28 +8480,28 @@ snapshots:
     optionalDependencies:
       fsevents: 2.3.3
 
-  rollup@4.26.0:
+  rollup@4.27.0:
     dependencies:
       '@types/estree': 1.0.6
     optionalDependencies:
-      '@rollup/rollup-android-arm-eabi': 4.26.0
-      '@rollup/rollup-android-arm64': 4.26.0
-      '@rollup/rollup-darwin-arm64': 4.26.0
-      '@rollup/rollup-darwin-x64': 4.26.0
-      '@rollup/rollup-freebsd-arm64': 4.26.0
-      '@rollup/rollup-freebsd-x64': 4.26.0
-      '@rollup/rollup-linux-arm-gnueabihf': 4.26.0
-      '@rollup/rollup-linux-arm-musleabihf': 4.26.0
-      '@rollup/rollup-linux-arm64-gnu': 4.26.0
-      '@rollup/rollup-linux-arm64-musl': 4.26.0
-      '@rollup/rollup-linux-powerpc64le-gnu': 4.26.0
-      '@rollup/rollup-linux-riscv64-gnu': 4.26.0
-      '@rollup/rollup-linux-s390x-gnu': 4.26.0
-      '@rollup/rollup-linux-x64-gnu': 4.26.0
-      '@rollup/rollup-linux-x64-musl': 4.26.0
-      '@rollup/rollup-win32-arm64-msvc': 4.26.0
-      '@rollup/rollup-win32-ia32-msvc': 4.26.0
-      '@rollup/rollup-win32-x64-msvc': 4.26.0
+      '@rollup/rollup-android-arm-eabi': 4.27.0
+      '@rollup/rollup-android-arm64': 4.27.0
+      '@rollup/rollup-darwin-arm64': 4.27.0
+      '@rollup/rollup-darwin-x64': 4.27.0
+      '@rollup/rollup-freebsd-arm64': 4.27.0
+      '@rollup/rollup-freebsd-x64': 4.27.0
+      '@rollup/rollup-linux-arm-gnueabihf': 4.27.0
+      '@rollup/rollup-linux-arm-musleabihf': 4.27.0
+      '@rollup/rollup-linux-arm64-gnu': 4.27.0
+      '@rollup/rollup-linux-arm64-musl': 4.27.0
+      '@rollup/rollup-linux-powerpc64le-gnu': 4.27.0
+      '@rollup/rollup-linux-riscv64-gnu': 4.27.0
+      '@rollup/rollup-linux-s390x-gnu': 4.27.0
+      '@rollup/rollup-linux-x64-gnu': 4.27.0
+      '@rollup/rollup-linux-x64-musl': 4.27.0
+      '@rollup/rollup-win32-arm64-msvc': 4.27.0
+      '@rollup/rollup-win32-ia32-msvc': 4.27.0
+      '@rollup/rollup-win32-x64-msvc': 4.27.0
       fsevents: 2.3.3
 
   rtl-css-js@1.16.1:
@@ -8637,7 +8637,7 @@ snapshots:
     dependencies:
       call-bind: 1.0.7
       define-properties: 1.2.1
-      es-abstract: 1.23.4
+      es-abstract: 1.23.5
       es-errors: 1.3.0
       es-object-atoms: 1.0.0
       get-intrinsic: 1.2.4
@@ -8652,7 +8652,7 @@ snapshots:
     dependencies:
       call-bind: 1.0.7
       define-properties: 1.2.1
-      es-abstract: 1.23.4
+      es-abstract: 1.23.5
       es-object-atoms: 1.0.0
 
   string.prototype.trimend@1.0.8:
@@ -8958,7 +8958,7 @@ snapshots:
     dependencies:
       esbuild: 0.21.5
       postcss: 8.4.49
-      rollup: 4.26.0
+      rollup: 4.27.0
     optionalDependencies:
       '@types/node': 20.17.6
       fsevents: 2.3.3
@@ -9150,7 +9150,7 @@ snapshots:
 
   yaml@1.10.2: {}
 
-  yet-another-react-lightbox@3.21.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+  yet-another-react-lightbox@3.21.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
     dependencies:
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
diff --git a/src/components/lightning/inline-invoice-card.tsx b/src/components/lightning/inline-invoice-card.tsx
index 44be05647..792a095dc 100644
--- a/src/components/lightning/inline-invoice-card.tsx
+++ b/src/components/lightning/inline-invoice-card.tsx
@@ -56,7 +56,7 @@ export default function InlineInvoiceCard({
 
   if (!invoice) return <>Loading Invoice...</>;
 
-  const isExpired = dayjs(invoice.expiry).isBefore(dayjs());
+  const isExpired = dayjs.unix(invoice.expiry).isBefore(dayjs());
 
   return (
     <Flex
@@ -87,7 +87,7 @@ export default function InlineInvoiceCard({
           </Box>
           <Box>
             <Text color={isExpired ? "red.400" : undefined}>
-              {isExpired ? "Expired" : "Expires"}: {dayjs(invoice.expiry).fromNow()}
+              {isExpired ? "Expired" : "Expires"}: {dayjs.unix(invoice.expiry).fromNow()}
             </Text>
           </Box>
           <ButtonGroup variant="outline">
diff --git a/src/components/note/note-zap-button.tsx b/src/components/note/note-zap-button.tsx
index 17acaace6..b78c23260 100644
--- a/src/components/note/note-zap-button.tsx
+++ b/src/components/note/note-zap-button.tsx
@@ -1,4 +1,5 @@
 import { Button, ButtonProps, IconButton, useDisclosure } from "@chakra-ui/react";
+import { getZapSender } from "applesauce-core/helpers";
 
 import { readablizeSats } from "../../helpers/bolt11";
 import { totalZaps } from "../../helpers/nostr/zaps";
@@ -11,7 +12,6 @@ import ZapModal from "../event-zap-modal";
 import useUserLNURLMetadata from "../../hooks/use-user-lnurl-metadata";
 import { getEventUID } from "../../helpers/nostr/event";
 import { useReadRelays } from "../../hooks/use-client-relays";
-import { getZapSender } from "applesauce-core/helpers";
 
 export type NoteZapButtonProps = Omit<ButtonProps, "children"> & {
   event: NostrEvent;
diff --git a/src/queries/dvm-responses.ts b/src/queries/dvm-responses.ts
new file mode 100644
index 000000000..334ddaddf
--- /dev/null
+++ b/src/queries/dvm-responses.ts
@@ -0,0 +1,20 @@
+import { Query } from "applesauce-core";
+import { NostrEvent } from "nostr-tools";
+import { scan } from "rxjs/operators";
+
+export default function DVMResponsesQuery(request: NostrEvent): Query<Record<string, NostrEvent>> {
+  return {
+    key: request.id,
+    run: (events) =>
+      events.stream([{ kinds: [request.kind + 1000, 7000], "#e": [request.id] }]).pipe(
+        scan(
+          (byPubkey, event) => {
+            if (byPubkey[event.pubkey] && byPubkey[event.pubkey].created_at > event.created_at) return byPubkey;
+
+            return { ...byPubkey, [event.pubkey]: event };
+          },
+          {} as Record<string, NostrEvent>,
+        ),
+      ),
+  };
+}
diff --git a/src/views/discovery/dvm-feed/components/feed-status.tsx b/src/views/discovery/dvm-feed/components/feed-status.tsx
index a93a7f259..fa01a8e14 100644
--- a/src/views/discovery/dvm-feed/components/feed-status.tsx
+++ b/src/views/discovery/dvm-feed/components/feed-status.tsx
@@ -9,26 +9,29 @@ import {
   CardBody,
   CardHeader,
   Code,
+  Flex,
   Heading,
   Spinner,
   Text,
 } from "@chakra-ui/react";
 import dayjs from "dayjs";
+import { NostrEvent } from "nostr-tools";
+import { getTagValue } from "applesauce-core/helpers";
+import { AddressPointer } from "nostr-tools/nip19";
 
-import {
-  ChainedDVMJob,
-  DVM_CONTENT_DISCOVERY_JOB_KIND,
-  getJobStatusType,
-  getResponseFromDVM,
-} from "../../../../helpers/nostr/dvm";
+import { ChainedDVMJob, DVM_CONTENT_DISCOVERY_JOB_KIND, getResponseFromDVM } from "../../../../helpers/nostr/dvm";
 import { DraftNostrEvent } from "../../../../types/nostr-event";
 import { useReadRelays } from "../../../../hooks/use-client-relays";
 import { DVMAvatarLink } from "./dvm-avatar";
 import DVMLink from "./dvm-name";
-import { AddressPointer } from "nostr-tools/nip19";
 import useUserMailboxes from "../../../../hooks/use-user-mailboxes";
 import { usePublishEvent } from "../../../../providers/global/publish-provider";
 import InlineInvoiceCard from "../../../../components/lightning/inline-invoice-card";
+import UserAvatar from "../../../../components/user/user-avatar";
+import UserLink from "../../../../components/user/user-link";
+import UserDnsIdentity from "../../../../components/user/user-dns-identity";
+import DebugEventButton from "../../../../components/debug-modal/debug-event-button";
+import NoteZapButton from "../../../../components/note/note-zap-button";
 
 function NextPageButton({ chain, pointer }: { pointer: AddressPointer; chain: ChainedDVMJob[] }) {
   const publish = usePublishEvent();
@@ -68,21 +71,31 @@ function NextPageButton({ chain, pointer }: { pointer: AddressPointer; chain: Ch
   );
 }
 
-export default function FeedStatus({ chain, pointer }: { chain: ChainedDVMJob[]; pointer: AddressPointer }) {
-  const lastJob = chain[chain.length - 1];
-  const response = lastJob.responses.find((r) => r.pubkey === pointer.pubkey);
-  if (response?.result) return <NextPageButton pointer={pointer} chain={chain} />;
-
+export function DVMStatusCard({ status, pointer }: { status?: NostrEvent; pointer?: AddressPointer }) {
   const cardProps = { w: "full", maxW: "2xl", mx: "auto", overflow: "hidden" };
   const cardHeader = (
     <CardHeader p="4" alignItems="center" display="flex" gap="2">
-      <DVMAvatarLink pointer={pointer} w="12" />
-      <DVMLink pointer={pointer} fontWeight="bold" fontSize="lg" />
+      {pointer ? (
+        <>
+          <DVMAvatarLink pointer={pointer} w="12" />
+          <DVMLink pointer={pointer} fontWeight="bold" fontSize="lg" />
+        </>
+      ) : (
+        status && (
+          <>
+            <UserAvatar pubkey={status.pubkey} size="md" />
+            <Flex direction="column">
+              <UserLink pubkey={status.pubkey} fontWeight="bold" fontSize="lg" />
+              <UserDnsIdentity pubkey={status.pubkey} />
+            </Flex>
+          </>
+        )
+      )}
+      {status && <DebugEventButton ml="auto" event={status} size="sm" variant="ghost" />}
     </CardHeader>
   );
 
-  const statusEvent = response?.status;
-  if (!statusEvent)
+  if (!status)
     return (
       <Card {...cardProps}>
         {cardHeader}
@@ -93,16 +106,16 @@ export default function FeedStatus({ chain, pointer }: { chain: ChainedDVMJob[];
       </Card>
     );
 
-  const statusType = getJobStatusType(lastJob, pointer.pubkey);
+  const statusType = getTagValue(status, "status");
   switch (statusType) {
     case "payment-required":
-      const [_, msats, invoice] = statusEvent.tags.find((t) => t[0] === "amount") ?? [];
+      const [_, msats, invoice] = status.tags.find((t) => t[0] === "amount") ?? [];
 
       return (
         <Card {...cardProps}>
           {cardHeader}
           <CardBody px="4" pb="4" pt="0" gap="2" display="flex" flexDirection="column">
-            <Heading size="sm">{statusEvent.content}</Heading>
+            <Heading size="sm">{status.content}</Heading>
             {invoice && <InlineInvoiceCard paymentRequest={invoice} />}
           </CardBody>
         </Card>
@@ -113,7 +126,7 @@ export default function FeedStatus({ chain, pointer }: { chain: ChainedDVMJob[];
           <AlertIcon boxSize={8} />
           <Box>
             <AlertTitle>Processing</AlertTitle>
-            <AlertDescription>{statusEvent.content}</AlertDescription>
+            <AlertDescription>{status.content}</AlertDescription>
           </Box>
         </Alert>
       );
@@ -123,7 +136,7 @@ export default function FeedStatus({ chain, pointer }: { chain: ChainedDVMJob[];
           <AlertIcon boxSize={8} />
           <Box>
             <AlertTitle>Error!</AlertTitle>
-            <AlertDescription>{statusEvent.content}</AlertDescription>
+            <AlertDescription>{status.content}</AlertDescription>
           </Box>
         </Alert>
       );
@@ -133,8 +146,16 @@ export default function FeedStatus({ chain, pointer }: { chain: ChainedDVMJob[];
           <Text>
             Unknown status <Code>{statusType}</Code>
           </Text>
-          <Text>{statusEvent.content}</Text>
+          <Text>{status.content}</Text>
         </>
       );
   }
 }
+
+export default function FeedStatus({ chain, pointer }: { chain: ChainedDVMJob[]; pointer: AddressPointer }) {
+  const lastJob = chain[chain.length - 1];
+  const response = lastJob.responses.find((r) => r.pubkey === pointer.pubkey);
+  if (response?.result) return <NextPageButton pointer={pointer} chain={chain} />;
+
+  return <DVMStatusCard status={response?.status} pointer={pointer} />;
+}
diff --git a/src/views/thread/components/details-tabs.tsx b/src/views/thread/components/details-tabs.tsx
index 7204b88f8..eccd2c30c 100644
--- a/src/views/thread/components/details-tabs.tsx
+++ b/src/views/thread/components/details-tabs.tsx
@@ -1,4 +1,5 @@
-import { Button, Flex } from "@chakra-ui/react";
+import { ReactNode } from "react";
+import { Tab, TabList, TabPanel, TabPanels, Tabs } from "@chakra-ui/react";
 import { kinds } from "nostr-tools";
 import { getEventUID } from "nostr-idb";
 import styled from "@emotion/styled";
@@ -15,11 +16,12 @@ import useTimelineLoader from "../../../hooks/use-timeline-loader";
 import { getContentTagRefs } from "../../../helpers/nostr/event";
 import { CORRECTION_EVENT_KIND } from "../../../helpers/nostr/corrections";
 import CorrectionsTab from "./tabs/corrections";
-import useRouteStateValue from "../../../hooks/use-route-state-value";
 import UnknownTab from "./tabs/unknown";
 import { repliesByDate } from "../../../helpers/thread";
+import ToolsTab from "./tabs/tools";
+import useRouteSearchValue from "../../../hooks/use-route-search-value";
 
-const HiddenScrollbar = styled(Flex)`
+const HiddenScrollbarTabList = styled(TabList)`
   -ms-overflow-style: none; /* IE and Edge */
   scrollbar-width: none; /* Firefox */
   &::-webkit-scrollbar {
@@ -28,7 +30,7 @@ const HiddenScrollbar = styled(Flex)`
 `;
 
 export default function DetailsTabs({ post }: { post: ThreadItem }) {
-  const { value: selected, setValue: setSelected } = useRouteStateValue("tab", "replies");
+  const selected = useRouteSearchValue("tab", "replies");
 
   const zaps = useEventZaps(getEventUID(post.event));
 
@@ -62,99 +64,121 @@ export default function DetailsTabs({ post }: { post: ThreadItem }) {
       !corrections.includes(e),
   );
 
-  const renderContent = () => {
-    switch (selected) {
-      case "replies":
-        return (
-          <Flex direction="column" gap="2" pl={{ base: 2, md: 4 }}>
-            {repliesByDate(post).map((child) => (
-              <ThreadPost key={child.event.id} post={child} focusId={undefined} level={0} />
-            ))}
-          </Flex>
-        );
-      case "quotes":
-        return <PostQuotesTab post={post} quotes={quotes} />;
-      case "reactions":
-        return <PostReactionsTab post={post} reactions={reactions} />;
-      case "reposts":
-        return <PostRepostsTab post={post} reposts={reposts} />;
-      case "zaps":
-        return <PostZapsTab post={post} zaps={zaps} />;
-      case "corrections":
-        return <CorrectionsTab post={post} corrections={corrections} />;
-      case "unknown":
-        return <UnknownTab post={post} events={unknown} />;
-    }
-    return null;
-  };
+  const tabs: { id: string; name: string; element: ReactNode; visible: boolean; right?: boolean }[] = [
+    {
+      id: "replies",
+      name: `Replies (${post.replies.size})`,
+      visible: true,
+      element: (
+        <TabPanel key="replies" display="flex" flexDirection="column" gap="2" py="2" pr="0" pl={{ base: 2, md: 4 }}>
+          {repliesByDate(post).map((child) => (
+            <ThreadPost key={child.event.id} post={child} focusId={undefined} level={0} />
+          ))}
+        </TabPanel>
+      ),
+    },
+    {
+      id: "quotes",
+      name: `Quotes (${quotes.length})`,
+      visible: quotes.length > 0,
+      element: (
+        <TabPanel key="quotes" p="0" py="2">
+          <PostQuotesTab post={post} quotes={quotes} />
+        </TabPanel>
+      ),
+    },
+    {
+      id: "zaps",
+      name: `Zaps (${zaps.length})`,
+      visible: zaps.length > 0,
+      element: (
+        <TabPanel key="zaps" p="0" py="2">
+          <PostZapsTab post={post} zaps={zaps} />
+        </TabPanel>
+      ),
+    },
+    {
+      id: "reposts",
+      name: `Reposts (${reposts.length})`,
+      visible: reposts.length > 0,
+      element: (
+        <TabPanel key="reposts" p="0" py="2">
+          <PostRepostsTab post={post} reposts={reposts} />
+        </TabPanel>
+      ),
+    },
+    {
+      id: "reactions",
+      name: `Reactions (${reactions.length})`,
+      visible: reactions.length > 0,
+      element: (
+        <TabPanel key="reactions" p="0">
+          <PostReactionsTab post={post} reactions={reactions} />
+        </TabPanel>
+      ),
+    },
+    {
+      id: "corrections",
+      name: `Corrections (${corrections.length})`,
+      visible: corrections.length > 0,
+      element: (
+        <TabPanel key="corrections" p="0">
+          <CorrectionsTab post={post} corrections={corrections} />
+        </TabPanel>
+      ),
+    },
+    {
+      id: "tools",
+      name: "Tools",
+      visible: true,
+      right: true,
+      element: (
+        <TabPanel key="tools" p="0">
+          <ToolsTab event={post.event} />
+        </TabPanel>
+      ),
+    },
+    {
+      id: "unknown",
+      name: `Unknown (${unknown.length})`,
+      visible: unknown.length > 0,
+      element: (
+        <TabPanel key="unknown" p="0" py="2">
+          <UnknownTab post={post} events={unknown} />
+        </TabPanel>
+      ),
+    },
+  ];
 
-  return (
-    <>
-      <HiddenScrollbar gap="4" px="2" overflowX="auto">
-        <Button
-          size="sm"
-          flexShrink={0}
-          variant={selected === "replies" ? "solid" : "outline"}
-          onClick={() => setSelected("replies")}
-        >
-          Replies{post.replies.size > 0 ? ` (${post.replies.size})` : ""}
-        </Button>
-        <Button
-          size="sm"
-          flexShrink={0}
-          variant={selected === "quotes" ? "solid" : "outline"}
-          onClick={() => setSelected("quotes")}
-        >
-          Quotes{quotes.length > 0 ? ` (${quotes.length})` : ""}
-        </Button>
-        <Button
-          size="sm"
-          flexShrink={0}
-          variant={selected === "zaps" ? "solid" : "outline"}
-          onClick={() => setSelected("zaps")}
-        >
-          Zaps{zaps.length > 0 ? ` (${zaps.length})` : ""}
-        </Button>
-        <Button
-          size="sm"
-          flexShrink={0}
-          variant={selected === "reposts" ? "solid" : "outline"}
-          onClick={() => setSelected("reposts")}
-        >
-          Reposts{reposts.length && reposts.length > 0 ? ` (${reposts.length})` : ""}
-        </Button>
-        <Button
-          size="sm"
-          flexShrink={0}
-          variant={selected === "reactions" ? "solid" : "outline"}
-          onClick={() => setSelected("reactions")}
-          mr="auto"
-        >
-          Reactions{reactions.length > 0 ? ` (${reactions.length})` : ""}
-        </Button>
-        {corrections.length > 0 && (
-          <Button
-            size="sm"
-            flexShrink={0}
-            variant={selected === "corrections" ? "solid" : "outline"}
-            onClick={() => setSelected("corrections")}
-          >
-            Corrections ({corrections.length})
-          </Button>
-        )}
-        {unknown.length > 0 && (
-          <Button
-            size="sm"
-            flexShrink={0}
-            variant={selected === "unknown" ? "solid" : "outline"}
-            onClick={() => setSelected("unknown")}
-          >
-            Unknown Refs ({unknown.length})
-          </Button>
-        )}
-      </HiddenScrollbar>
+  const s = tabs.find((t) => t.id === selected.value);
+  const index = s ? tabs.indexOf(s) : 0;
 
-      {renderContent()}
-    </>
+  return (
+    <Tabs
+      display="flex"
+      flexDirection="column"
+      flexGrow="1"
+      isLazy
+      index={index}
+      onChange={(v) => {
+        if (tabs[v]) selected.setValue(tabs[v].id, true);
+        else selected.clearValue(true);
+      }}
+      h="full"
+      colorScheme="primary"
+      variant="solid-rounded"
+      size="sm"
+    >
+      <HiddenScrollbarTabList px="2" gap="2" whiteSpace="pre" overflowX="auto">
+        {tabs
+          .filter((t) => t.visible)
+          .map((tab) => (
+            <Tab key={tab.id} ml={tab.right ? "auto" : undefined}>
+              {tab.name}
+            </Tab>
+          ))}
+      </HiddenScrollbarTabList>
+      <TabPanels minH={{ base: "50vh", md: "0" }}>{tabs.filter((t) => t.visible).map((tab) => tab.element)}</TabPanels>
+    </Tabs>
   );
 }
diff --git a/src/views/thread/components/reply-form.tsx b/src/views/thread/components/reply-form.tsx
index ab0c5a6bf..0798b2d20 100644
--- a/src/views/thread/components/reply-form.tsx
+++ b/src/views/thread/components/reply-form.tsx
@@ -30,7 +30,7 @@ import { useTextAreaUploadFileWithForm } from "../../../hooks/use-textarea-uploa
 export type ReplyFormProps = {
   item: ThreadItem;
   replyKind?: number;
-  onCancel: () => void;
+  onCancel?: () => void;
   onSubmitted?: (event: NostrEvent) => void;
 };
 
@@ -104,7 +104,7 @@ export default function ReplyForm({ item, onCancel, onSubmitted, replyKind = kin
         />
         <UserAvatarStack label="Notify" pubkeys={notifyPubkeys} />
         <ButtonGroup size="sm" ml="auto">
-          <Button onClick={onCancel}>Cancel</Button>
+          {onCancel && <Button onClick={onCancel}>Cancel</Button>}
           <Button type="submit" colorScheme="primary" size="sm">
             Submit
           </Button>
diff --git a/src/views/thread/components/tabs/tools.tsx b/src/views/thread/components/tabs/tools.tsx
new file mode 100644
index 000000000..f94707fd2
--- /dev/null
+++ b/src/views/thread/components/tabs/tools.tsx
@@ -0,0 +1,61 @@
+import { ReactNode } from "react";
+import { Button, ComponentWithAs, Flex, Heading, IconButton, IconProps } from "@chakra-ui/react";
+import { NostrEvent } from "nostr-tools";
+
+import Translate01 from "../../../../components/icons/translate-01";
+import Recording02 from "../../../../components/icons/recording-02";
+import PenTool01 from "../../../../components/icons/pen-tool-01";
+import { NoteTranslationsPage } from "../../../tools/transform-note/translation";
+import useRouteSearchValue from "../../../../hooks/use-route-search-value";
+import NoteTextToSpeechPage from "../../../tools/transform-note/text-to-speech";
+import EventSummarizePage from "./tools/summary";
+
+const tools: {
+  icon: ComponentWithAs<"svg", IconProps>;
+  id: string;
+  name: string;
+  render: (event: NostrEvent) => ReactNode;
+}[] = [
+  { id: "translate", icon: Translate01, name: "Translate", render: (event) => <NoteTranslationsPage note={event} /> },
+  { id: "tts", icon: Recording02, name: "Text to speech", render: (event) => <NoteTextToSpeechPage note={event} /> },
+  { id: "summarize", icon: PenTool01, name: "Summarize", render: (event) => <EventSummarizePage event={event} /> },
+];
+
+export default function ToolsTab({ event }: { event: NostrEvent }) {
+  const selected = useRouteSearchValue("tool", "");
+  const tool = tools.find((t) => t.id === selected.value);
+
+  const IconComponent = tool?.icon;
+
+  return (
+    <Flex direction="column" gap="2" p="2" w="full">
+      <Flex gap="2" alignItems="center" wrap="wrap">
+        {tool && IconComponent ? (
+          <IconButton
+            icon={<IconComponent boxSize={6} />}
+            aria-label="Select Tool"
+            onClick={() => selected.clearValue(true)}
+          />
+        ) : (
+          <>
+            {tools.map(({ icon: Icon, name, id }) => (
+              <Button
+                variant="outline"
+                key={id}
+                leftIcon={<Icon boxSize={10} mb="4" />}
+                onClick={() => selected.setValue(id, true)}
+                h="36"
+                w="36"
+                flexDirection="column"
+              >
+                {name}
+              </Button>
+            ))}
+          </>
+        )}
+        {tool && <Heading size="md">{tool.name}</Heading>}
+      </Flex>
+      {tool?.render(event)}
+    </Flex>
+  );
+}
diff --git a/src/views/thread/components/tabs/tools/summary.tsx b/src/views/thread/components/tabs/tools/summary.tsx
new file mode 100644
index 000000000..eb39d65a9
--- /dev/null
+++ b/src/views/thread/components/tabs/tools/summary.tsx
@@ -0,0 +1,101 @@
+import { useEffect, useState } from "react";
+import { useForm } from "react-hook-form";
+import { Button, Flex, Spinner, Text, Textarea, useToast } from "@chakra-ui/react";
+import { EventTemplate, NostrEvent } from "nostr-tools";
+import { MultiSubscription } from "applesauce-net/subscription";
+import { useStoreQuery } from "applesauce-react/hooks";
+import { unixNow } from "applesauce-core/helpers";
+
+import { useUserInbox } from "../../../../../hooks/use-user-mailboxes";
+import useCurrentAccount from "../../../../../hooks/use-current-account";
+import { usePublishEvent } from "../../../../../providers/global/publish-provider";
+import relayPoolService from "../../../../../services/relay-pool";
+import { eventStore } from "../../../../../services/event-store";
+import DVMResponsesQuery from "../../../../../queries/dvm-responses";
+import { DVMStatusCard } from "../../../../discovery/dvm-feed/components/feed-status";
+
+function PromptForm({ onSubmit }: { onSubmit: (prompt: string) => void | Promise<void> }) {
+  const { register, handleSubmit } = useForm({ defaultValues: { prompt: "" } });
+
+  const submit = handleSubmit(async (values) => {
+    await onSubmit(values.prompt);
+  });
+
+  return (
+    <Flex gap="2" direction="column" as="form" onSubmit={submit}>
+      <Textarea {...register("prompt")} placeholder="Prompt an AI model to summarize note" />
+      <Button ml="auto" type="submit" size="sm" colorScheme="primary">
+        Summarize
+      </Button>
+    </Flex>
+  );
+}
+
+export default function EventSummarizePage({ event }: { event: NostrEvent }) {
+  const toast = useToast();
+  const [submitted, setSubmitted] = useState(false);
+  const [request, setRequest] = useState<NostrEvent>();
+
+  const publish = usePublishEvent();
+  const account = useCurrentAccount();
+  const inbox = useUserInbox(account?.pubkey);
+
+  const newRequest = async (prompt: string) => {
+    try {
+      if (!inbox) throw new Error("Missing user inbox relays");
+
+      const draft: EventTemplate = {
+        kind: 5001,
+        content: "",
+        tags: [
+          ["relays", ...inbox],
+          ["i", event.id, "event"],
+          ["output", "text/plain"],
+        ],
+        created_at: unixNow(),
+      };
+
+      // add human prompt
+      if (prompt) draft.tags.unshift(["i", prompt, "text"]);
+
+      const pub = await publish("Summarize Event", draft);
+      setRequest(pub?.event);
+      setSubmitted(true);
+    } catch (error) {
+      if (error instanceof Error) toast({ description: error.message, status: "error" });
+    }
+  };
+
+  useEffect(() => {
+    if (!inbox || !request) return;
+
+    const sub = new MultiSubscription(relayPoolService);
+    sub.onEvent.subscribe((e) => eventStore.add(e));
+
+    sub.setFilters([{ kinds: [7000, 6001], "#e": [request.id] }]);
+    sub.setRelays(inbox);
+    sub.open();
+
+    return () => sub.close();
+  }, [request]);
+
+  const responses = useStoreQuery(DVMResponsesQuery, request ? [request] : undefined);
+
+  return (
+    <Flex direction="column" gap="2" px="2">
+      {responses ? (
+        <>
+          {Object.entries(responses).map(([pubkey, event]) => (
+            <DVMStatusCard status={event} />
+          ))}
+        </>
+      ) : submitted ? (
+        <Text>
+          <Spinner /> Waiting for responses...
+        </Text>
+      ) : (
+        <PromptForm onSubmit={newRequest} />
+      )}
+    </Flex>
+  );
+}
diff --git a/src/views/thread/index.tsx b/src/views/thread/index.tsx
index 17ea3a36a..025966c49 100644
--- a/src/views/thread/index.tsx
+++ b/src/views/thread/index.tsx
@@ -40,7 +40,7 @@ function CollapsedReplies({
   return (
     <>
       {reply}
-      <Card gap="2" overflow="hidden" px="2" display="flex" flexDirection="row" p="2">
+      <Card gap="2" overflow="hidden" px="2" display="flex" flexDirection="row" p="2" flexShrink={0}>
         <UserAvatarLink pubkey={post.event.pubkey} size="xs" />
         <UserName pubkey={post.event.pubkey} fontWeight="bold" />
         {root.id !== pointer.id && <ReplyIcon />}
diff --git a/src/views/tools/transform-note/translation/translation-result.tsx b/src/views/tools/transform-note/translation/translation-result.tsx
index c454074a3..2092d415c 100644
--- a/src/views/tools/transform-note/translation/translation-result.tsx
+++ b/src/views/tools/transform-note/translation/translation-result.tsx
@@ -5,6 +5,7 @@ import UserLink from "../../../../components/user/user-link";
 import { NostrEvent } from "../../../../types/nostr-event";
 import TextNoteContents from "../../../../components/note/timeline-note/text-note-contents";
 import { TrustProvider } from "../../../../providers/local/trust-provider";
+import DebugEventButton from "../../../../components/debug-modal/debug-event-button";
 
 export default function TranslationResult({ result }: { result: NostrEvent }) {
   const content = useDisclosure();
@@ -18,6 +19,7 @@ export default function TranslationResult({ result }: { result: NostrEvent }) {
         <Button size="sm" onClick={content.onToggle}>
           {content.isOpen ? "Hide" : "Show"} Content
         </Button>
+        <DebugEventButton ml="auto" event={result} size="sm" variant="ghost" />
       </Flex>
       {content.isOpen && (
         <TrustProvider trust>