@lostgradient/cinder 0.7.0 → 0.9.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 (279) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +7 -2
  3. package/components.json +59 -15
  4. package/dist/components/_internal/command-list-context.d.ts +3 -3
  5. package/dist/components/_internal/create-command-list-state.svelte.d.ts +11 -11
  6. package/dist/components/access-gate/index.js +201 -126
  7. package/dist/components/approval-card/approval-card-actions.svelte.d.ts +16 -0
  8. package/dist/components/approval-card/approval-card-state.d.ts +65 -0
  9. package/dist/components/approval-card/approval-card.css +269 -92
  10. package/dist/components/approval-card/approval-card.schema.js +10 -31
  11. package/dist/components/approval-card/approval-card.svelte.d.ts +3 -3
  12. package/dist/components/approval-card/approval-card.types.d.ts +14 -28
  13. package/dist/components/approval-card/index.d.ts +1 -1
  14. package/dist/components/approval-card/index.js +4268 -2884
  15. package/dist/components/card/card.css +5 -0
  16. package/dist/components/card/card.schema.js +2 -2
  17. package/dist/components/card/card.types.d.ts +2 -2
  18. package/dist/components/card/index.js +2 -1
  19. package/dist/components/chat/chat.schema.js +48 -1
  20. package/dist/components/chat/chat.svelte.d.ts +6 -2
  21. package/dist/components/chat/chat.types.d.ts +37 -0
  22. package/dist/components/chat/container/chat.svelte.d.ts +6 -2
  23. package/dist/components/chat/index.d.ts +3 -3
  24. package/dist/components/chat/index.js +1099 -4865
  25. package/dist/components/chat/input/chat-attachment-serialization.d.ts +9 -0
  26. package/dist/components/chat/input/chat-input.svelte.d.ts +24 -0
  27. package/dist/components/chat/input/index.d.ts +1 -0
  28. package/dist/components/chat-composer-popover/chat-composer-popover-filter.d.ts +11 -0
  29. package/dist/components/chat-composer-popover/chat-composer-popover.css +7 -0
  30. package/dist/components/chat-composer-popover/chat-composer-popover.css.d.ts +1 -0
  31. package/dist/components/chat-composer-popover/chat-composer-popover.schema.d.ts +3 -0
  32. package/dist/components/chat-composer-popover/chat-composer-popover.schema.js +150 -0
  33. package/dist/components/chat-composer-popover/chat-composer-popover.svelte.d.ts +46 -0
  34. package/dist/components/chat-composer-popover/chat-composer-popover.types.d.ts +77 -0
  35. package/dist/components/chat-composer-popover/chat-composer-popover.variables.d.ts +2 -0
  36. package/dist/components/chat-composer-popover/chat-composer-popover.variables.js +61 -0
  37. package/dist/components/chat-composer-popover/index.d.ts +6 -0
  38. package/dist/components/chat-composer-popover/index.js +3285 -0
  39. package/dist/components/chat-conversation-header/index.js +1627 -5600
  40. package/dist/components/chat-conversation-list/index.js +15 -3988
  41. package/dist/components/code-block/code-block.schema.js +5 -1
  42. package/dist/components/code-block/code-block.types.d.ts +6 -0
  43. package/dist/components/code-block/index.js +4 -4
  44. package/dist/components/command-item/index.js +7 -6
  45. package/dist/components/command-menu/command-menu.schema.js +5 -1
  46. package/dist/components/command-menu/command-menu.types.d.ts +2 -0
  47. package/dist/components/command-menu/index.js +48 -19
  48. package/dist/components/command-palette/index.js +40 -14
  49. package/dist/components/event-stream-viewer/index.js +44 -44
  50. package/dist/components/experimental/json-viewer/index.js +44 -44
  51. package/dist/components/json-schema-editor/index.js +5 -2
  52. package/dist/components/json-viewer/index.js +44 -44
  53. package/dist/components/json-viewer/json-viewer.css +10 -6
  54. package/dist/components/navigation-bar/index.js +44 -6
  55. package/dist/components/payload-inspector/index.d.ts +1 -1
  56. package/dist/components/payload-inspector/index.js +306 -1422
  57. package/dist/components/payload-inspector/payload-inspector.css +15 -75
  58. package/dist/components/payload-inspector/payload-inspector.schema.js +5 -41
  59. package/dist/components/payload-inspector/payload-inspector.svelte.d.ts +5 -5
  60. package/dist/components/payload-inspector/payload-inspector.types.d.ts +10 -56
  61. package/dist/components/sidebar/index.js +6 -3
  62. package/dist/components/sidebar/sidebar.css +4 -0
  63. package/dist/components/tab/index.js +5 -2
  64. package/dist/components/tab/tab.schema.js +7 -3
  65. package/dist/components/tab/tab.svelte.d.ts +2 -1
  66. package/dist/components/tab/tab.types.d.ts +8 -2
  67. package/dist/components/tabs/index.js +5 -2
  68. package/dist/components/team-section/index.js +2 -1
  69. package/dist/components/testimonial-section/index.js +2 -1
  70. package/dist/index.d.ts +4 -2
  71. package/dist/index.js +24169 -27821
  72. package/dist/server/components/access-gate/index.js +2 -1
  73. package/dist/server/components/accordion/index.js +2 -2
  74. package/dist/server/components/alert-dialog/index.js +2 -2
  75. package/dist/server/components/approval-card/index.js +15 -19
  76. package/dist/server/components/autocomplete/index.js +7 -7
  77. package/dist/server/components/avatar-group/index.js +5 -5
  78. package/dist/server/components/button-group/index.js +3 -5
  79. package/dist/server/components/card/index.js +2 -2
  80. package/dist/server/components/chat/index.js +14 -11
  81. package/dist/server/components/chat-composer-popover/index.js +26 -0
  82. package/dist/server/components/chat-conversation-header/index.js +9 -10
  83. package/dist/server/components/chat-conversation-list/index.js +4 -5
  84. package/dist/server/components/checkbox/index.js +3 -3
  85. package/dist/server/components/choice-grid/index.js +2 -2
  86. package/dist/server/components/code-block/index.js +2 -2
  87. package/dist/server/components/color-field/index.js +3 -3
  88. package/dist/server/components/combobox/index.js +6 -6
  89. package/dist/server/components/command-item/index.js +5 -3
  90. package/dist/server/components/command-menu/index.js +6 -4
  91. package/dist/server/components/command-palette/index.js +2 -2
  92. package/dist/server/components/confirm-dialog/index.js +2 -2
  93. package/dist/server/components/context-menu/index.js +5 -5
  94. package/dist/server/components/data-table/index.js +5 -5
  95. package/dist/server/components/date-picker/index.js +6 -6
  96. package/dist/server/components/date-range-field/index.js +7 -7
  97. package/dist/server/components/description-list/index.js +3 -5
  98. package/dist/server/components/diff-viewer/index.js +7 -7
  99. package/dist/server/components/drawer/index.js +3 -3
  100. package/dist/server/components/dropdown/index.js +4 -4
  101. package/dist/server/components/event-stream-viewer/index.js +2 -2
  102. package/dist/server/components/experimental/json-viewer/index.js +2 -2
  103. package/dist/server/components/faceted-filter-bar/index.js +1 -1
  104. package/dist/server/components/hover-card/index.js +1 -1
  105. package/dist/server/components/input/index.js +3 -3
  106. package/dist/server/components/json-schema-editor/index.js +22 -22
  107. package/dist/server/components/json-viewer/index.js +2 -2
  108. package/dist/server/components/markdown-editor/index.js +13 -13
  109. package/dist/server/components/modal/index.js +1 -1
  110. package/dist/server/components/multi-select/index.js +1 -1
  111. package/dist/server/components/navigation-bar/index.js +1 -1
  112. package/dist/server/components/newsletter-section/index.js +3 -3
  113. package/dist/server/components/payload-inspector/index.js +4 -16
  114. package/dist/server/components/popover/index.js +5 -5
  115. package/dist/server/components/review-editor/index.js +25 -25
  116. package/dist/server/components/run-step-timeline/index.js +2 -2
  117. package/dist/server/components/schema-form/index.js +4 -4
  118. package/dist/server/components/secret-value-field/index.js +3 -5
  119. package/dist/server/components/segment/index.js +3 -3
  120. package/dist/server/components/segmented-control/index.js +4 -4
  121. package/dist/server/components/selection-popover/index.js +4 -4
  122. package/dist/server/components/sheet/index.js +2 -2
  123. package/dist/server/components/sidebar/index.js +3 -3
  124. package/dist/server/components/tab/index.js +2 -2
  125. package/dist/server/components/table/index.js +3 -3
  126. package/dist/server/components/tabs/index.js +4 -4
  127. package/dist/server/components/tag-input/index.js +3 -3
  128. package/dist/server/components/team-section/index.js +8 -8
  129. package/dist/server/components/testimonial-section/index.js +2 -2
  130. package/dist/server/components/toggle/index.js +2 -2
  131. package/dist/server/components/tooltip/index.js +1 -1
  132. package/dist/server/index.js +160 -149
  133. package/dist/server/{index.server-cbtg8rq5.js → index.server-0atyznp7.js} +5 -5
  134. package/dist/server/{index.server-20y4mdhm.js → index.server-0chhpkg7.js} +40 -14
  135. package/dist/server/{index.server-ecm1vs3g.js → index.server-1nq7z1qw.js} +1 -1
  136. package/dist/server/{index.server-jync915v.js → index.server-2dga5yzr.js} +25 -25
  137. package/dist/server/index.server-2hqrv7ca.js +9 -0
  138. package/dist/server/{index.server-qwknp759.js → index.server-2qj6s2mh.js} +7 -4
  139. package/dist/server/{index.server-pt8b65mt.js → index.server-38aywdk1.js} +1 -1
  140. package/dist/server/{index.server-rermas0k.js → index.server-47v2fhp4.js} +4 -4
  141. package/dist/server/{index.server-9j5sym59.js → index.server-4x91gzy1.js} +2 -2
  142. package/dist/server/{index.server-q48q15g7.js → index.server-4y2jtvv1.js} +4 -2
  143. package/dist/server/{index.server-zeqv7nrz.js → index.server-5tf22d6e.js} +43 -6
  144. package/dist/server/index.server-67qgdypc.js +9 -0
  145. package/dist/server/{index.server-9jete4xx.js → index.server-6sh11ap6.js} +4 -4
  146. package/dist/server/{index.server-4ztq565m.js → index.server-85p4ydph.js} +1 -1
  147. package/dist/server/{index.server-4nk7gy0q.js → index.server-894t97kv.js} +1 -1
  148. package/dist/server/{index.server-1vscnped.js → index.server-8dt36sdm.js} +2 -1
  149. package/dist/server/{index.server-bfsbp9dz.js → index.server-8xvc73zh.js} +4 -4
  150. package/dist/server/{index.server-cvnw3hw9.js → index.server-9kz37z89.js} +1 -1
  151. package/dist/server/{index.server-kc1k02tk.js → index.server-9ykk1kbg.js} +1 -1
  152. package/dist/server/{index.server-g789yep6.js → index.server-aqethy0w.js} +2 -2
  153. package/dist/server/{index.server-wc9aggfz.js → index.server-bmxbsk4y.js} +7 -7
  154. package/dist/server/{index.server-j3ddzcaa.js → index.server-c6pe4kaf.js} +6 -6
  155. package/dist/server/{index.server-q89dz8h1.js → index.server-d3pqthtp.js} +4 -3
  156. package/dist/server/{index.server-qfa2rxzx.js → index.server-dg83ke3k.js} +3 -5
  157. package/dist/server/{index.server-jk4rx4aa.js → index.server-dmmvwy6p.js} +1 -1
  158. package/dist/server/{index.server-hcfnae5y.js → index.server-dwwqssey.js} +4 -4
  159. package/dist/server/{index.server-e8x1hgw6.js → index.server-dz1r9xw5.js} +4 -4
  160. package/dist/server/{index.server-wm7mk5kw.js → index.server-ez5hp0ya.js} +1 -1
  161. package/dist/server/{index.server-ejyf7e48.js → index.server-fy068yne.js} +1 -1
  162. package/dist/server/{index.server-wc8dbm5e.js → index.server-g95bk8rc.js} +4 -2
  163. package/dist/server/index.server-gkazv439.js +576 -0
  164. package/dist/server/{index.server-dp9j2kkr.js → index.server-h0cvwvhy.js} +1 -1
  165. package/dist/server/{index.server-pacrqvyz.js → index.server-h90pjrez.js} +1 -1
  166. package/dist/server/{index.server-p8d04em5.js → index.server-hcsd5gdb.js} +6 -2
  167. package/dist/server/{index.server-n39drzes.js → index.server-hpeavvnq.js} +1 -1
  168. package/dist/server/{index.server-1p3patnj.js → index.server-hx91ng7z.js} +1 -1
  169. package/dist/server/{index.server-jtj3r53y.js → index.server-jgb8yh5k.js} +1 -1
  170. package/dist/server/{index.server-w1g5q9j9.js → index.server-jtgw9d23.js} +4 -2
  171. package/dist/server/{index.server-mehav5vs.js → index.server-jxg42jpz.js} +4 -4
  172. package/dist/server/{index.server-hd8gpehd.js → index.server-jxseyx33.js} +1 -1
  173. package/dist/server/{index.server-kg2sepsr.js → index.server-krqvnt6p.js} +14 -15
  174. package/dist/server/{index.server-0e1m3agz.js → index.server-ks49bprh.js} +1 -1
  175. package/dist/server/{index.server-f0hma3jp.js → index.server-kt0ncrqs.js} +1 -1
  176. package/dist/server/index.server-m0ezemav.js +170 -0
  177. package/dist/server/index.server-m191rm44.js +79 -0
  178. package/dist/server/{index.server-f23vtaw4.js → index.server-marjz8ps.js} +6 -6
  179. package/dist/server/index.server-mqt1ngqw.js +250 -0
  180. package/dist/server/{index.server-s9khh4ry.js → index.server-mzzb7dnm.js} +3 -5
  181. package/dist/server/{index.server-z721a5gp.js → index.server-n82rcv0m.js} +4 -4
  182. package/dist/server/{index.server-rhhmdxtz.js → index.server-np3c7kx0.js} +4 -4
  183. package/dist/server/{index.server-hpcs7tmh.js → index.server-p56xxef1.js} +1 -1
  184. package/dist/server/{index.server-w83kmcrx.js → index.server-pag862vz.js} +10 -10
  185. package/dist/server/{index.server-gxf0d1p5.js → index.server-q7w4jw89.js} +2 -2
  186. package/dist/server/{index.server-bvmwnjx6.js → index.server-qh9qzd5t.js} +186 -23
  187. package/dist/server/{index.server-pxg74pyp.js → index.server-qk1h0n02.js} +4 -4
  188. package/dist/server/{index.server-sh2rxejg.js → index.server-qke3ekjt.js} +1 -1
  189. package/dist/server/{index.server-73x2d3j3.js → index.server-rbpvcrr3.js} +1 -1
  190. package/dist/server/{index.server-8cxs4kya.js → index.server-rga8sc7j.js} +41 -44
  191. package/dist/server/{index.server-e3fnv1g6.js → index.server-sedjxt36.js} +5 -5
  192. package/dist/server/{index.server-pen9pxt6.js → index.server-sf3nk3j2.js} +2 -2
  193. package/dist/server/{index.server-7fp0epev.js → index.server-veastatv.js} +4 -4
  194. package/dist/server/{index.server-jeqg2d22.js → index.server-xeqtpv61.js} +1 -1
  195. package/dist/server/{index.server-9s4na701.js → index.server-xjnge1yw.js} +4 -4
  196. package/dist/server/index.server-xtjjmsyq.js +315 -0
  197. package/dist/server/{index.server-hhxjaeen.js → index.server-xz99aed4.js} +4 -4
  198. package/dist/server/{index.server-vne4rzfa.js → index.server-y4n8vc08.js} +1 -1
  199. package/dist/server/{index.server-5d01cn4f.js → index.server-y83kqayt.js} +4 -4
  200. package/dist/server/{index.server-1nh09ck7.js → index.server-yhzydqxe.js} +5 -5
  201. package/dist/server/{index.server-atjc1qcq.js → index.server-yz4k36cw.js} +1 -1
  202. package/dist/server/{index.server-w6k62afr.js → index.server-ze1ymmbw.js} +2 -2
  203. package/dist/server/{index.server-914v805w.js → index.server-zgd1g8mk.js} +1 -1
  204. package/dist/server/{index.server-aeexcxdp.js → index.server-zh3r8vfd.js} +1 -1
  205. package/dist/server/{index.server-10jb2jzm.js → index.server-zsjf8enc.js} +4 -4
  206. package/package.json +42 -6
  207. package/src/components/_internal/command-list-context.ts +3 -3
  208. package/src/components/_internal/create-command-list-state.svelte.ts +39 -13
  209. package/src/components/approval-card/approval-card-actions.svelte +168 -0
  210. package/src/components/approval-card/approval-card-state.ts +152 -1
  211. package/src/components/approval-card/approval-card.css +269 -92
  212. package/src/components/approval-card/approval-card.examples.json +3 -3
  213. package/src/components/approval-card/approval-card.schema.json +9 -30
  214. package/src/components/approval-card/approval-card.schema.ts +13 -31
  215. package/src/components/approval-card/approval-card.svelte +176 -470
  216. package/src/components/approval-card/approval-card.types.ts +15 -28
  217. package/src/components/approval-card/index.ts +1 -0
  218. package/src/components/card/card.css +5 -0
  219. package/src/components/card/card.schema.json +1 -1
  220. package/src/components/card/card.schema.ts +1 -1
  221. package/src/components/card/card.svelte +1 -0
  222. package/src/components/card/card.types.ts +2 -2
  223. package/src/components/chat/chat.schema.json +40 -0
  224. package/src/components/chat/chat.schema.ts +49 -0
  225. package/src/components/chat/chat.svelte +20 -0
  226. package/src/components/chat/chat.types.ts +38 -0
  227. package/src/components/chat/container/chat.svelte +82 -3
  228. package/src/components/chat/index.ts +15 -3
  229. package/src/components/chat/input/chat-attachment-serialization.ts +63 -0
  230. package/src/components/chat/input/chat-input.svelte +30 -3
  231. package/src/components/chat/input/index.ts +5 -0
  232. package/src/components/chat-composer-popover/chat-composer-popover-filter.ts +68 -0
  233. package/src/components/chat-composer-popover/chat-composer-popover.css +7 -0
  234. package/src/components/chat-composer-popover/chat-composer-popover.examples.json +13 -0
  235. package/src/components/chat-composer-popover/chat-composer-popover.schema.json +90 -0
  236. package/src/components/chat-composer-popover/chat-composer-popover.schema.ts +95 -0
  237. package/src/components/chat-composer-popover/chat-composer-popover.svelte +234 -0
  238. package/src/components/chat-composer-popover/chat-composer-popover.types.ts +88 -0
  239. package/src/components/chat-composer-popover/chat-composer-popover.variables.json +1 -0
  240. package/src/components/chat-composer-popover/chat-composer-popover.variables.ts +3 -0
  241. package/src/components/chat-composer-popover/index.ts +19 -0
  242. package/src/components/code-block/code-block.schema.json +4 -0
  243. package/src/components/code-block/code-block.schema.ts +5 -0
  244. package/src/components/code-block/code-block.svelte +3 -3
  245. package/src/components/code-block/code-block.types.ts +6 -0
  246. package/src/components/command-item/command-item.svelte +7 -6
  247. package/src/components/command-menu/command-menu.schema.json +4 -0
  248. package/src/components/command-menu/command-menu.schema.ts +4 -0
  249. package/src/components/command-menu/command-menu.svelte +16 -5
  250. package/src/components/command-menu/command-menu.types.ts +2 -0
  251. package/src/components/json-viewer/_json-viewer-node.svelte +15 -1
  252. package/src/components/json-viewer/json-viewer.css +10 -6
  253. package/src/components/navigation-bar/navigation-bar.svelte +49 -7
  254. package/src/components/payload-inspector/index.ts +0 -2
  255. package/src/components/payload-inspector/payload-inspector.css +15 -75
  256. package/src/components/payload-inspector/payload-inspector.examples.json +5 -5
  257. package/src/components/payload-inspector/payload-inspector.schema.json +3 -31
  258. package/src/components/payload-inspector/payload-inspector.schema.ts +3 -36
  259. package/src/components/payload-inspector/payload-inspector.svelte +46 -239
  260. package/src/components/payload-inspector/payload-inspector.types.ts +10 -58
  261. package/src/components/sidebar/sidebar.css +4 -0
  262. package/src/components/sidebar/sidebar.svelte +6 -3
  263. package/src/components/tab/tab.schema.json +6 -2
  264. package/src/components/tab/tab.schema.ts +8 -2
  265. package/src/components/tab/tab.svelte +9 -5
  266. package/src/components/tab/tab.types.ts +8 -2
  267. package/src/index.ts +18 -5
  268. package/src/styles/components.css +1 -0
  269. package/dist/server/index-49we2xfs.js +0 -344
  270. package/dist/server/index-8fhvxk30.js +0 -265
  271. package/dist/server/index-x2ysg8ds.js +0 -388
  272. package/dist/server/index.server-20j0jh30.js +0 -9
  273. package/dist/server/index.server-dc35kh7d.js +0 -1835
  274. package/dist/server/index.server-kd4b0342.js +0 -607
  275. package/dist/server/index.server-n5cdmsz4.js +0 -30
  276. package/dist/server/index.server-rjap4k7y.js +0 -1389
  277. package/dist/server/index.server-vt6sn9y4.js +0 -447
  278. package/dist/server/index.server-w63n618v.js +0 -9
  279. package/dist/server/index.server-y2pa1k46.js +0 -9
@@ -3,46 +3,57 @@ export {};
3
3
  </script>
4
4
 
5
5
  <script lang="ts">
6
- import { onDestroy } from 'svelte';
6
+ import SignalHigh from 'lucide-svelte/icons/signal-high';
7
+ import SignalLow from 'lucide-svelte/icons/signal-low';
8
+ import SignalMedium from 'lucide-svelte/icons/signal-medium';
7
9
  import { classNames } from '../../utilities/class-names.ts';
8
10
  import Badge from '../badge/badge.svelte';
9
- import Button from '../button/button.svelte';
10
- import ButtonGroup from '../button-group/button-group.svelte';
11
11
  import Card from '../card/card.svelte';
12
12
  import CodeBlock from '../code-block/code-block.svelte';
13
+ import Collapsible from '../collapsible/collapsible.svelte';
14
+ import CopyButton from '../copy-button/copy-button.svelte';
13
15
  import PayloadInspector from '../payload-inspector/payload-inspector.svelte';
14
- import SecretValueField from '../secret-value-field/secret-value-field.svelte';
15
16
  import StatusDot from '../status-dot/status-dot.svelte';
16
- import { isApprovalActionable, resolveEffectiveApprovalState } from './approval-card-state.ts';
17
- const ARGUMENTS_PREVIEW_MAX_CHARACTERS = 4_096;
17
+ import Tooltip from '../tooltip/tooltip.svelte';
18
+ import ApprovalCardActions from './approval-card-actions.svelte';
19
+ import { RISK_LABELS, STATE_DOT_STATUS, STATE_LABELS, dedupeFilePaths, formatRemainingTime, isApprovalActionable, normalizeArgumentsPreviewValue, parseExpirationTimestamp, prepareArgumentsPreview, resolveEffectiveApprovalState, sanitizeEnvironmentNames, } from './approval-card-state.ts';
20
+ // Stacked-bar signal icon in place of a text badge: the shape itself scans
21
+ // faster than reading "Medium risk", and the tooltip/aria-label carries the
22
+ // same word for anyone who needs it named explicitly.
23
+ const RISK_ICONS = {
24
+ low: SignalLow,
25
+ medium: SignalMedium,
26
+ high: SignalHigh,
27
+ };
18
28
  const generatedId = $props.id();
19
- let { tool, sandbox, operation, env = [], snapshotId, policyVersion, idempotencyKey, expiresAt, state: approvalState, editableArgs = false, onresolve, onapprove, onapprovewithedits, ondeny, onremember, oncancel, id, class: customClassName, ...rest } = $props();
29
+ let { tool, sandbox, operation, env = [], snapshotId, policyVersion, idempotencyKey, expiresAt, state: approvalState, editableArgs = false, headingLevel = 3, onresolve, id, class: customClassName, ...rest } = $props();
20
30
  const rootId = $derived(id ?? generatedId);
21
31
  const titleId = $derived(`${rootId}-title`);
22
- const descriptionId = $derived(`${rootId}-description`);
23
- const resolutionDescriptionId = $derived(`${rootId}-resolution-description`);
24
- const editDescriptionId = $derived(`${rootId}-edit-description`);
25
- const editErrorId = $derived(`${rootId}-edit-error`);
32
+ // Coerce + clamp like Card: schema-driven callers can pass out-of-range or
33
+ // non-numeric levels, and `h${level}` must never emit invalid markup.
34
+ const resolvedHeadingLevel = $derived(Number.isFinite(Math.trunc(Number(headingLevel)))
35
+ ? Math.min(6, Math.max(2, Math.trunc(Number(headingLevel))))
36
+ : 3);
37
+ const titleTag = $derived(`h${resolvedHeadingLevel}`);
38
+ const sectionTag = $derived(`h${Math.min(6, resolvedHeadingLevel + 1)}`);
26
39
  let currentTime = $state();
27
- let resolutionReason = $state('');
28
- let rememberResolution = $state(false);
29
- let resolutionSeedKey = $state(null);
30
- let editingArgumentsSeedKey = $state(null);
31
- let editedArgumentsDrafts = $state({});
32
40
  let expirationTimer;
33
41
  const expirationTimestamp = $derived(parseExpirationTimestamp(expiresAt));
34
42
  const effectiveState = $derived(resolveEffectiveApprovalState(approvalState, expirationTimestamp, currentTime));
35
43
  const isActionable = $derived(isApprovalActionable(approvalState, expirationTimestamp, currentTime));
36
- const operationKindLabel = $derived(formatOperationKind(operation.kind));
37
- const riskLabel = $derived(formatRisk(tool.risk));
38
- const stateText = $derived(formatState(effectiveState));
39
- const stateDescription = $derived(isActionable
40
- ? `${operationKindLabel} approval is waiting for a decision.`
41
- : `No approval actions are available because this request is ${stateText.toLowerCase()}.`);
44
+ // Guards against a non-function truthy value reaching a JS (non-typechecked)
45
+ // or schema-driven caller — `onresolve?.()` alone would still throw on a
46
+ // truthy non-function.
47
+ const hasResolutionCallback = $derived(typeof onresolve === 'function');
48
+ const riskLabel = $derived(RISK_LABELS[tool.risk]);
49
+ const RiskIcon = $derived(RISK_ICONS[tool.risk]);
50
+ const stateText = $derived(STATE_LABELS[effectiveState]);
51
+ const readonlySummary = $derived(`No approval actions are available because this request is ${stateText.toLowerCase()}.`);
52
+ // Only a live countdown is shown. Once the state itself reads "Expired" (via
53
+ // StatusDot's label), a second "Expired" badge would just repeat the same word.
42
54
  const expirationText = $derived.by(() => {
43
- if (effectiveState === 'expired')
44
- return 'Expired';
45
55
  if (approvalState !== 'pending' ||
56
+ effectiveState !== 'pending' ||
46
57
  expirationTimestamp === undefined ||
47
58
  currentTime === undefined) {
48
59
  return undefined;
@@ -52,24 +63,10 @@ const expirationText = $derived.by(() => {
52
63
  const hasArgumentsPreview = $derived(operation.argsPreview !== undefined);
53
64
  const editableArgumentsValue = $derived(normalizeArgumentsPreviewValue(operation.argsPreview));
54
65
  const argumentsPreview = $derived(prepareArgumentsPreview(editableArgumentsValue));
55
- const filesTouched = $derived(operation.filesTouched ?? []);
66
+ const canEditArguments = $derived(editableArgs && hasArgumentsPreview);
67
+ const filesTouched = $derived(dedupeFilePaths(operation.filesTouched ?? []));
56
68
  const filesTouchedLabel = $derived(`${filesTouched.length} file${filesTouched.length === 1 ? '' : 's'}`);
57
69
  const environmentNames = $derived(sanitizeEnvironmentNames(env));
58
- const currentEditedArgumentsText = $derived(formatEditableArguments(editableArgumentsValue));
59
- const currentEditedArgumentsSeedKey = $derived(`${idempotencyKey}\u0000${currentEditedArgumentsText}`);
60
- const currentResolutionSeedKey = $derived(`${idempotencyKey}\u0000${operation.kind}`);
61
- const canEditArguments = $derived(editableArgs && hasArgumentsPreview);
62
- const editingArguments = $derived(canEditArguments && editingArgumentsSeedKey === currentEditedArgumentsSeedKey);
63
- const editedArgumentsText = $derived(editedArgumentsDrafts[currentEditedArgumentsSeedKey] ?? currentEditedArgumentsText);
64
- const editParseResult = $derived(parseJsonText(editedArgumentsText));
65
- const canConfirmEditedApproval = $derived(canEditArguments &&
66
- editParseResult.ok &&
67
- (typeof onapprovewithedits === 'function' || typeof onresolve === 'function'));
68
- const hasResolutionCallback = $derived(typeof onresolve === 'function');
69
- const canResolveApproval = $derived(typeof onapprove === 'function' || hasResolutionCallback);
70
- const canResolveEditedApproval = $derived(typeof onapprovewithedits === 'function' || hasResolutionCallback);
71
- const canResolveDenial = $derived(typeof ondeny === 'function' || hasResolutionCallback);
72
- const canResolveCancellation = $derived(typeof oncancel === 'function' || hasResolutionCallback);
73
70
  $effect(() => {
74
71
  clearExpirationTimer();
75
72
  if (approvalState === 'pending' && expirationTimestamp !== undefined) {
@@ -87,254 +84,32 @@ $effect(() => {
87
84
  }
88
85
  return clearExpirationTimer;
89
86
  });
90
- $effect(() => {
91
- if (resolutionSeedKey !== currentResolutionSeedKey) {
92
- resolutionSeedKey = currentResolutionSeedKey;
93
- resolutionReason = '';
94
- rememberResolution = false;
95
- }
96
- });
97
- onDestroy(clearExpirationTimer);
98
87
  function clearExpirationTimer() {
99
88
  if (expirationTimer !== undefined) {
100
89
  clearTimeout(expirationTimer);
101
90
  expirationTimer = undefined;
102
91
  }
103
92
  }
104
- function parseExpirationTimestamp(value) {
105
- if (!value)
106
- return undefined;
107
- const timestamp = Date.parse(value);
108
- return Number.isFinite(timestamp) ? timestamp : undefined;
109
- }
110
- function formatRemainingTime(milliseconds) {
111
- const totalSeconds = Math.max(0, Math.ceil(milliseconds / 1_000));
112
- const hours = Math.floor(totalSeconds / 3_600);
113
- const minutes = Math.floor((totalSeconds % 3_600) / 60);
114
- const seconds = totalSeconds % 60;
115
- if (hours > 0)
116
- return `${hours}h ${minutes}m`;
117
- if (minutes > 0)
118
- return `${minutes}m ${seconds}s`;
119
- return `${seconds}s`;
120
- }
121
- function formatRisk(risk) {
122
- switch (risk) {
123
- case 'low':
124
- return 'Low risk';
125
- case 'medium':
126
- return 'Medium risk';
127
- case 'high':
128
- return 'High risk';
129
- }
130
- }
131
- function riskBadgeVariant(risk) {
132
- switch (risk) {
133
- case 'low':
134
- return 'success';
135
- case 'medium':
136
- return 'warning';
137
- case 'high':
138
- return 'danger';
139
- }
140
- }
141
- function formatOperationKind(kind) {
142
- switch (kind) {
143
- case 'command':
144
- return 'Command';
145
- case 'file-write':
146
- return 'File write';
147
- case 'patch':
148
- return 'Patch';
149
- case 'other':
150
- return 'Operation';
151
- }
152
- }
153
- function formatState(approvalState) {
154
- switch (approvalState) {
155
- case 'pending':
156
- return 'Pending';
157
- case 'approved':
158
- return 'Approved';
159
- case 'approved_with_edits':
160
- return 'Approved with edits';
161
- case 'denied':
162
- return 'Denied';
163
- case 'expired':
164
- return 'Expired';
165
- case 'cancelled':
166
- return 'Cancelled';
167
- }
168
- }
169
- function stateDotStatus(approvalState) {
170
- switch (approvalState) {
171
- case 'pending':
172
- return 'pending';
173
- case 'approved':
174
- case 'approved_with_edits':
175
- return 'success';
176
- case 'denied':
177
- return 'danger';
178
- case 'expired':
179
- return 'neutral';
180
- case 'cancelled':
181
- return 'offline';
182
- }
183
- }
184
- function normalizeArgumentsPreviewValue(value) {
185
- if (typeof value !== 'string')
186
- return value;
187
- try {
188
- return JSON.parse(value);
189
- }
190
- catch {
191
- return value;
192
- }
193
- }
194
- function prepareArgumentsPreview(value) {
195
- try {
196
- const serialized = JSON.stringify(value);
197
- if (typeof serialized !== 'string' || serialized.length <= ARGUMENTS_PREVIEW_MAX_CHARACTERS) {
198
- return {
199
- value: typeof value === 'string' ? serialized : value,
200
- truncated: false,
201
- };
202
- }
203
- return {
204
- value: {
205
- notice: 'Arguments preview truncated',
206
- originalCharacters: serialized.length,
207
- displayedCharacters: ARGUMENTS_PREVIEW_MAX_CHARACTERS,
208
- excerpt: serialized.slice(0, ARGUMENTS_PREVIEW_MAX_CHARACTERS),
209
- },
210
- truncated: true,
211
- };
212
- }
213
- catch {
214
- return { value, truncated: false };
215
- }
216
- }
217
- function formatEditableArguments(value) {
218
- try {
219
- const serialized = JSON.stringify(value, null, 2);
220
- return serialized ?? 'null';
221
- }
222
- catch {
223
- return '{}';
224
- }
225
- }
226
- function parseJsonText(text) {
227
- try {
228
- return { ok: true, value: JSON.parse(text) };
229
- }
230
- catch {
231
- return { ok: false, message: 'Edited arguments must be valid JSON.' };
232
- }
233
- }
234
- function sanitizeEnvironmentName(name) {
235
- const [firstPart] = name.split('=');
236
- return firstPart?.trim() ?? '';
237
- }
238
- function sanitizeEnvironmentNames(names) {
239
- const seen = [];
240
- const sanitizedNames = [];
241
- for (const name of names) {
242
- const sanitizedName = sanitizeEnvironmentName(name);
243
- if (!sanitizedName || seen.includes(sanitizedName))
244
- continue;
245
- seen.push(sanitizedName);
246
- sanitizedNames.push(sanitizedName);
247
- }
248
- return sanitizedNames;
249
- }
250
- function currentApprovalIsActionable() {
93
+ function resolveIfActionable(resolution) {
94
+ // Re-check against the wall clock: the countdown timer only ticks once per
95
+ // second, so a click can land after the deadline but before the next tick.
251
96
  const comparisonTime = expirationTimestamp === undefined ? currentTime : Date.now();
252
- const actionable = isApprovalActionable(approvalState, expirationTimestamp, comparisonTime);
253
- if (!actionable && approvalState === 'pending' && expirationTimestamp !== undefined) {
254
- currentTime = comparisonTime;
255
- }
256
- return actionable;
257
- }
258
- function beginEditingArguments() {
259
- if (!currentApprovalIsActionable())
260
- return;
261
- if (!canEditArguments)
97
+ if (!isApprovalActionable(approvalState, expirationTimestamp, comparisonTime)) {
98
+ if (approvalState === 'pending' && expirationTimestamp !== undefined) {
99
+ currentTime = comparisonTime;
100
+ }
262
101
  return;
263
- if (!(currentEditedArgumentsSeedKey in editedArgumentsDrafts)) {
264
- editedArgumentsDrafts = {
265
- ...editedArgumentsDrafts,
266
- [currentEditedArgumentsSeedKey]: currentEditedArgumentsText,
267
- };
268
- }
269
- editingArgumentsSeedKey = currentEditedArgumentsSeedKey;
270
- }
271
- function handleEditedArgumentsInput(event) {
272
- editedArgumentsDrafts = {
273
- ...editedArgumentsDrafts,
274
- [currentEditedArgumentsSeedKey]: event.currentTarget.value,
275
- };
276
- }
277
- function handleReasonInput(event) {
278
- resolutionReason = event.currentTarget.value;
279
- }
280
- function handleRememberInput(event) {
281
- rememberResolution = event.currentTarget.checked;
282
- }
283
- function emitResolution(decision, editedArgs) {
284
- const trimmedReason = resolutionReason.trim();
285
- const resolution = {
286
- decision,
287
- remember: rememberResolution,
288
- };
289
- if (decision === 'approve_with_edits') {
290
- resolution.editedArgs = editedArgs;
291
102
  }
292
- if (trimmedReason) {
293
- resolution.reason = trimmedReason;
294
- }
295
- onresolve?.(resolution);
296
- }
297
- function handleApprove() {
298
- if (!currentApprovalIsActionable())
299
- return;
300
- onapprove?.();
301
- emitResolution('approve');
302
- }
303
- function handleApproveWithEdits() {
304
- if (!currentApprovalIsActionable())
305
- return;
306
- if (!canEditArguments)
307
- return;
308
- if (!editParseResult.ok)
309
- return;
310
- onapprovewithedits?.(editParseResult.value);
311
- emitResolution('approve_with_edits', editParseResult.value);
312
- }
313
- function handleDeny() {
314
- if (!currentApprovalIsActionable())
315
- return;
316
- ondeny?.();
317
- emitResolution('deny');
318
- }
319
- function handleRemember() {
320
- if (!currentApprovalIsActionable())
321
- return;
322
- onremember?.();
323
- }
324
- function handleCancel() {
325
- if (!currentApprovalIsActionable())
326
- return;
327
- oncancel?.();
328
- emitResolution('cancel');
103
+ if (typeof onresolve === 'function')
104
+ onresolve(resolution);
329
105
  }
330
106
  </script>
331
107
 
332
- <section
108
+ <article
333
109
  {...rest}
334
110
  id={rootId}
335
111
  class={classNames('cinder-approval-card', customClassName)}
336
112
  aria-labelledby={titleId}
337
- aria-describedby={descriptionId}
338
113
  data-cinder-state={effectiveState}
339
114
  data-cinder-risk={tool.risk}
340
115
  >
@@ -342,19 +117,26 @@ function handleCancel() {
342
117
  {#snippet header()}
343
118
  <div class="cinder-approval-card__header">
344
119
  <div class="cinder-approval-card__title-group">
345
- <p class="cinder-approval-card__eyebrow">{operationKindLabel} approval</p>
346
- <h3 id={titleId} class="cinder-approval-card__title">
347
- Approval required for {tool.name}
348
- </h3>
349
- <p id={descriptionId} class="cinder-approval-card__description">
350
- {stateDescription}
351
- </p>
120
+ <svelte:element this={titleTag} id={titleId} class="cinder-approval-card__title">
121
+ Approval required for
122
+ <span class="cinder-approval-card__title-tool">{tool.name}</span>
123
+ </svelte:element>
352
124
  </div>
353
125
  <div class="cinder-approval-card__badges" role="group" aria-label="Approval status">
354
- <StatusDot status={stateDotStatus(effectiveState)} label={stateText} />
355
- <Badge variant={riskBadgeVariant(tool.risk)} size="sm">{riskLabel}</Badge>
126
+ <StatusDot status={STATE_DOT_STATUS[effectiveState]} label={stateText} size="sm" />
127
+ <Tooltip text={riskLabel} describe={false}>
128
+ <span
129
+ class="cinder-approval-card__risk-icon"
130
+ role="img"
131
+ tabindex="0"
132
+ aria-label={riskLabel}
133
+ data-cinder-risk={tool.risk}
134
+ >
135
+ <RiskIcon size={16} strokeWidth={2.25} aria-hidden="true" />
136
+ </span>
137
+ </Tooltip>
356
138
  {#if expirationText}
357
- <Badge variant={effectiveState === 'expired' ? 'neutral' : 'warning'} size="sm">
139
+ <Badge variant="warning" size="sm">
358
140
  {expirationText}
359
141
  </Badge>
360
142
  {/if}
@@ -363,213 +145,137 @@ function handleCancel() {
363
145
  {/snippet}
364
146
 
365
147
  <div class="cinder-approval-card__body">
366
- <dl class="cinder-approval-card__metadata" aria-label="Approval metadata">
367
- <div class="cinder-approval-card__metadata-item">
368
- <dt>Policy</dt>
369
- <dd>{policyVersion}</dd>
148
+ {#if operation.kind === 'command' && operation.command}
149
+ <div class="cinder-approval-card__section">
150
+ <CodeBlock code={operation.command} language="shell" showLanguageLabel={false} />
370
151
  </div>
371
- <div class="cinder-approval-card__metadata-item">
372
- <dt>Idempotency key</dt>
373
- <dd>{idempotencyKey}</dd>
152
+ {:else if operation.kind === 'patch' && operation.diff}
153
+ <div class="cinder-approval-card__section">
154
+ <CodeBlock code={operation.diff} language="diff" showLanguageLabel={false} />
374
155
  </div>
375
- {#if snapshotId}
376
- <div class="cinder-approval-card__metadata-item">
377
- <dt>Snapshot</dt>
378
- <dd>{snapshotId}</dd>
379
- </div>
380
- {/if}
381
- </dl>
382
-
383
- {#if sandbox}
384
- <section class="cinder-approval-card__section" aria-label="Sandbox context">
385
- <h4 class="cinder-approval-card__section-title">Sandbox</h4>
386
- <dl class="cinder-approval-card__metadata cinder-approval-card__metadata--compact">
387
- <div class="cinder-approval-card__metadata-item">
388
- <dt>Provider</dt>
389
- <dd>{sandbox.provider}</dd>
390
- </div>
391
- <div class="cinder-approval-card__metadata-item">
392
- <dt>Name</dt>
393
- <dd>{sandbox.name}</dd>
394
- </div>
395
- <div class="cinder-approval-card__metadata-item">
396
- <dt>Working directory</dt>
397
- <dd>{sandbox.workingDir}</dd>
398
- </div>
399
- </dl>
400
- </section>
401
156
  {/if}
402
157
 
403
- <section class="cinder-approval-card__section" aria-label="Operation details">
404
- <div class="cinder-approval-card__section-heading">
405
- <h4 class="cinder-approval-card__section-title">Operation</h4>
406
- <Badge size="sm" mono>{operation.kind}</Badge>
407
- </div>
408
-
409
- {#if operation.kind === 'command' && operation.command}
410
- <CodeBlock code={operation.command} language="shell" highlight={false} />
411
- {:else if operation.kind === 'patch' && operation.diff}
412
- <CodeBlock code={operation.diff} language="diff" highlight={false} />
413
- {:else if operation.kind === 'file-write'}
414
- <p class="cinder-approval-card__muted">
415
- Review the touched files and arguments before approving this file write.
416
- </p>
417
- {:else}
418
- <p class="cinder-approval-card__muted">
419
- Review the arguments and metadata before approving this operation.
420
- </p>
421
- {/if}
422
-
423
- {#if filesTouched.length > 0}
424
- <div class="cinder-approval-card__files">
425
- <div class="cinder-approval-card__section-heading">
426
- <h5 class="cinder-approval-card__subsection-title">Files touched</h5>
427
- <Badge size="sm">{filesTouchedLabel}</Badge>
428
- </div>
429
- <ul class="cinder-approval-card__file-list">
430
- {#each filesTouched as file, fileIndex (`${fileIndex}:${file}`)}
431
- <li>{file}</li>
432
- {/each}
433
- </ul>
158
+ {#if filesTouched.length > 0}
159
+ <div class="cinder-approval-card__section">
160
+ <div class="cinder-approval-card__section-heading">
161
+ <svelte:element this={sectionTag} class="cinder-approval-card__section-title">
162
+ Files touched
163
+ </svelte:element>
164
+ <Badge size="sm">{filesTouchedLabel}</Badge>
434
165
  </div>
435
- {/if}
436
- </section>
166
+ <ul class="cinder-approval-card__file-list">
167
+ {#each filesTouched as file (file)}
168
+ <li class="cinder-approval-card__file">
169
+ <span class="cinder-approval-card__file-path">{file}</span>
170
+ <CopyButton
171
+ value={file}
172
+ label={`Copy path ${file}`}
173
+ copiedLabel="Path copied"
174
+ class="cinder-approval-card__file-copy"
175
+ iconOnly
176
+ />
177
+ </li>
178
+ {/each}
179
+ </ul>
180
+ </div>
181
+ {/if}
437
182
 
438
- <section class="cinder-approval-card__section" aria-label="Arguments preview">
439
- {#if hasArgumentsPreview}
183
+ {#if hasArgumentsPreview}
184
+ <div class="cinder-approval-card__section">
440
185
  <PayloadInspector
441
186
  value={argumentsPreview.value}
442
187
  truncated={argumentsPreview.truncated}
443
- label="Arguments preview"
188
+ label={`${tool.name} arguments`}
444
189
  />
445
- {:else}
446
- <p class="cinder-approval-card__muted">No arguments were provided.</p>
447
- {/if}
448
- </section>
449
-
450
- {#if environmentNames.length > 0}
451
- <section class="cinder-approval-card__section" aria-label="Environment names">
452
- <h4 class="cinder-approval-card__section-title">Environment</h4>
453
- <div class="cinder-approval-card__environment-list">
454
- {#each environmentNames as environmentName, environmentIndex (`${environmentIndex}:${environmentName}`)}
455
- <SecretValueField value={environmentName} label={environmentName} />
456
- {/each}
457
- </div>
458
- </section>
190
+ </div>
459
191
  {/if}
460
192
 
461
- {#if isActionable}
462
- <section class="cinder-approval-card__section cinder-approval-card__actions-section">
463
- {#if hasResolutionCallback}
464
- <div class="cinder-approval-card__resolution-controls">
465
- <label class="cinder-approval-card__editor-label" for={`${rootId}-resolution-reason`}>
466
- Resolution reason
467
- </label>
468
- <p id={resolutionDescriptionId} class="cinder-approval-card__muted">
469
- Optional context included with the host resolution payload.
470
- </p>
471
- <textarea
472
- id={`${rootId}-resolution-reason`}
473
- class="cinder-approval-card__textarea cinder-approval-card__textarea--reason"
474
- value={resolutionReason}
475
- oninput={handleReasonInput}
476
- rows="3"
477
- aria-describedby={resolutionDescriptionId}
478
- ></textarea>
479
- <label class="cinder-approval-card__remember-control">
480
- <input
481
- type="checkbox"
482
- checked={rememberResolution}
483
- onchange={handleRememberInput}
484
- />
485
- <span>Remember this approval boundary</span>
486
- </label>
193
+ <Collapsible trigger="Details" class="cinder-approval-card__details">
194
+ <div class="cinder-approval-card__details-content">
195
+ {#if sandbox}
196
+ <div class="cinder-approval-card__details-group">
197
+ <p class="cinder-approval-card__details-label">Sandbox</p>
198
+ <dl class="cinder-approval-card__metadata cinder-approval-card__metadata--compact">
199
+ <div class="cinder-approval-card__metadata-item">
200
+ <dt>Provider</dt>
201
+ <dd>{sandbox.provider}</dd>
202
+ </div>
203
+ <div class="cinder-approval-card__metadata-item">
204
+ <dt>Profile</dt>
205
+ <dd>{sandbox.name}</dd>
206
+ </div>
207
+ <div class="cinder-approval-card__metadata-item">
208
+ <dt>Working directory</dt>
209
+ <dd>{sandbox.workingDir}</dd>
210
+ </div>
211
+ </dl>
487
212
  </div>
488
213
  {/if}
489
214
 
490
- <ButtonGroup label="Approval actions">
491
- <Button
492
- type="button"
493
- variant="primary"
494
- onclick={handleApprove}
495
- disabled={!canResolveApproval}
496
- >
497
- Approve
498
- </Button>
499
- {#if canEditArguments}
500
- <Button
501
- type="button"
502
- variant="secondary"
503
- onclick={beginEditingArguments}
504
- disabled={!canResolveEditedApproval}
215
+ {#if environmentNames.length > 0}
216
+ <div class="cinder-approval-card__details-group">
217
+ <p class="cinder-approval-card__details-label">Environment</p>
218
+ <ul
219
+ class="cinder-approval-card__environment-list"
220
+ aria-label="Environment variable names only; values are never shown"
505
221
  >
506
- Approve with edits
507
- </Button>
508
- {/if}
509
- <Button
510
- type="button"
511
- variant="soft-danger"
512
- onclick={handleDeny}
513
- disabled={!canResolveDenial}
514
- >
515
- Deny
516
- </Button>
517
- <Button type="button" variant="soft" onclick={handleRemember} disabled={!onremember}>
518
- Remember
519
- </Button>
520
- <Button
521
- type="button"
522
- variant="ghost"
523
- onclick={handleCancel}
524
- disabled={!canResolveCancellation}
525
- >
526
- Cancel
527
- </Button>
528
- </ButtonGroup>
529
-
530
- {#if editingArguments}
531
- <div class="cinder-approval-card__editor">
532
- <label class="cinder-approval-card__editor-label" for={`${rootId}-edited-arguments`}>
533
- Edited arguments JSON
534
- </label>
535
- <p id={editDescriptionId} class="cinder-approval-card__muted">
536
- Confirming parses this JSON and sends the parsed value to the approval callback.
537
- </p>
538
- <textarea
539
- id={`${rootId}-edited-arguments`}
540
- class="cinder-approval-card__textarea"
541
- value={editedArgumentsText}
542
- oninput={handleEditedArgumentsInput}
543
- rows="8"
544
- spellcheck="false"
545
- aria-describedby={editParseResult.ok
546
- ? editDescriptionId
547
- : `${editDescriptionId} ${editErrorId}`}
548
- aria-invalid={editParseResult.ok ? undefined : 'true'}
549
- ></textarea>
550
- {#if !editParseResult.ok}
551
- <p id={editErrorId} class="cinder-approval-card__error" role="alert">
552
- {editParseResult.message}
553
- </p>
554
- {/if}
555
- <div class="cinder-approval-card__editor-actions">
556
- <Button
557
- type="button"
558
- variant="primary"
559
- onclick={handleApproveWithEdits}
560
- disabled={!canConfirmEditedApproval}
561
- >
562
- Confirm edited approval
563
- </Button>
564
- </div>
222
+ {#each environmentNames as environmentName (environmentName)}
223
+ <li>
224
+ <Badge size="sm" mono>{environmentName}</Badge>
225
+ </li>
226
+ {/each}
227
+ </ul>
565
228
  </div>
566
229
  {/if}
567
- </section>
568
- {:else}
569
- <p class="cinder-approval-card__readonly-summary">
570
- No approval actions are available for this request.
230
+
231
+ <dl class="cinder-approval-card__metadata">
232
+ <div class="cinder-approval-card__metadata-item">
233
+ <dt>Policy version</dt>
234
+ <dd>
235
+ {policyVersion}
236
+ <span class="cinder-approval-card__metadata-help">
237
+ Approval policy that produced this request.
238
+ </span>
239
+ </dd>
240
+ </div>
241
+ <div class="cinder-approval-card__metadata-item">
242
+ <dt>Idempotency key</dt>
243
+ <dd>
244
+ {idempotencyKey}
245
+ <span class="cinder-approval-card__metadata-help">
246
+ Keeps this decision durable if it is submitted more than once.
247
+ </span>
248
+ </dd>
249
+ </div>
250
+ {#if snapshotId}
251
+ <div class="cinder-approval-card__metadata-item">
252
+ <dt>Snapshot</dt>
253
+ <dd>
254
+ {snapshotId}
255
+ <span class="cinder-approval-card__metadata-help">
256
+ Workspace snapshot this request was captured against.
257
+ </span>
258
+ </dd>
259
+ </div>
260
+ {/if}
261
+ </dl>
262
+ </div>
263
+ </Collapsible>
264
+
265
+ {#if isActionable && hasResolutionCallback}
266
+ <ApprovalCardActions
267
+ idBase={rootId}
268
+ requestKey={idempotencyKey}
269
+ {canEditArguments}
270
+ argumentsValue={editableArgumentsValue}
271
+ resolve={resolveIfActionable}
272
+ />
273
+ {/if}
274
+ {#if !isActionable}
275
+ <p class="cinder-approval-card__readonly-summary" data-cinder-state={effectiveState}>
276
+ {readonlySummary}
571
277
  </p>
572
278
  {/if}
573
279
  </div>
574
280
  </Card>
575
- </section>
281
+ </article>