@opencode-ai/ui 0.0.0-dev-19219 → 0.0.0-dev-202609070344

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 (299) hide show
  1. package/dist/{data-display/accordion → components}/accordion.d.ts +6 -6
  2. package/dist/components/avatar.d.ts +9 -0
  3. package/dist/components/button.d.ts +9 -0
  4. package/dist/components/card.d.ts +1 -1
  5. package/dist/{forms/checkbox → components}/checkbox.d.ts +2 -2
  6. package/dist/components/dialog.d.ts +12 -0
  7. package/dist/{data-display/diff-changes → components}/diff-changes.d.ts +1 -2
  8. package/dist/components/dropdown-menu.d.ts +80 -0
  9. package/dist/components/icon-button.d.ts +10 -0
  10. package/dist/{icons/icon/additional-icons.d.ts → components/icon.d.ts} +8 -8
  11. package/dist/components/keybind.d.ts +6 -0
  12. package/dist/components/list.d.ts +1 -1
  13. package/dist/{feedback/progress-circle → components}/progress-circle.d.ts +0 -2
  14. package/dist/components/radio-group.d.ts +16 -0
  15. package/dist/components/scroll-view.d.ts +4 -14
  16. package/dist/components/select.d.ts +21 -0
  17. package/dist/{forms/switch → components}/switch.d.ts +2 -4
  18. package/dist/{navigation/tabs → components}/tabs.d.ts +8 -15
  19. package/dist/components/tag.d.ts +5 -0
  20. package/dist/{typography/text-shimmer → components}/text-shimmer.d.ts +0 -1
  21. package/dist/components/toast.d.ts +56 -0
  22. package/dist/components/tooltip.d.ts +17 -0
  23. package/dist/context/i18n.d.ts +7 -10
  24. package/dist/context/marked-parser.d.ts +2 -1
  25. package/dist/hooks/use-filtered-list.d.ts +0 -9
  26. package/dist/i18n/ar.d.ts +2 -0
  27. package/dist/i18n/bg.d.ts +2 -0
  28. package/dist/i18n/br.d.ts +2 -0
  29. package/dist/i18n/bs.d.ts +2 -0
  30. package/dist/i18n/da.d.ts +2 -0
  31. package/dist/i18n/de.d.ts +2 -0
  32. package/dist/i18n/en.d.ts +1 -242
  33. package/dist/i18n/es.d.ts +2 -0
  34. package/dist/i18n/fr.d.ts +2 -0
  35. package/dist/i18n/ja.d.ts +2 -0
  36. package/dist/i18n/km.d.ts +2 -0
  37. package/dist/i18n/ko.d.ts +2 -0
  38. package/dist/i18n/lo.d.ts +2 -0
  39. package/dist/i18n/mk.d.ts +2 -0
  40. package/dist/i18n/mn.d.ts +2 -0
  41. package/dist/i18n/my.d.ts +2 -0
  42. package/dist/i18n/pl.d.ts +2 -0
  43. package/dist/i18n/ru.d.ts +2 -0
  44. package/dist/i18n/sr.d.ts +2 -0
  45. package/dist/i18n/tg.d.ts +2 -0
  46. package/dist/i18n/th.d.ts +2 -0
  47. package/dist/i18n/tr.d.ts +2 -0
  48. package/dist/i18n/zh.d.ts +2 -0
  49. package/dist/i18n/zht.d.ts +2 -0
  50. package/dist/v2/components/accordion-v2.d.ts +26 -0
  51. package/dist/{data-display/avatar/avatar.d.ts → v2/components/avatar-v2.d.ts} +1 -1
  52. package/dist/v2/components/badge-v2.d.ts +6 -0
  53. package/dist/v2/components/button-v2.d.ts +10 -0
  54. package/dist/v2/components/checkbox-v2.d.ts +10 -0
  55. package/dist/{overlays/dialog/dialog.d.ts → v2/components/dialog-v2.d.ts} +2 -1
  56. package/dist/v2/components/diff-changes-v2.d.ts +11 -0
  57. package/dist/v2/components/divider-v2.d.ts +5 -0
  58. package/dist/{forms/field/field.d.ts → v2/components/field-v2.d.ts} +10 -4
  59. package/dist/v2/components/icon-button-v2.d.ts +11 -0
  60. package/dist/{icons/icon → v2/components}/icon.d.ts +1 -52
  61. package/dist/v2/components/inline-input-v2.d.ts +21 -0
  62. package/dist/v2/components/keybind-v2.d.ts +7 -0
  63. package/dist/{data-display/line-comment/line-comment.d.ts → v2/components/line-comment-v2.d.ts} +9 -9
  64. package/dist/v2/components/loader-v2.d.ts +3 -0
  65. package/dist/v2/components/menu-v2.d.ts +52 -0
  66. package/dist/v2/components/progress-circle-v2.d.ts +8 -0
  67. package/dist/{data-display/project-avatar/project-avatar.d.ts → v2/components/project-avatar-v2.d.ts} +1 -1
  68. package/dist/v2/components/radio-v2.d.ts +16 -0
  69. package/dist/{navigation/segmented-control/segmented-control.d.ts → v2/components/segmented-control-v2.d.ts} +5 -5
  70. package/dist/{forms/select/select.d.ts → v2/components/select-v2.d.ts} +6 -5
  71. package/dist/v2/components/split-button-v2.d.ts +5 -0
  72. package/dist/v2/components/switch-v2.d.ts +7 -0
  73. package/dist/v2/components/tabs-v2.d.ts +34 -0
  74. package/dist/{forms/text-input/text-input.d.ts → v2/components/text-input-v2.d.ts} +3 -3
  75. package/dist/v2/components/text-shimmer-v2.d.ts +9 -0
  76. package/dist/v2/components/textarea-v2.d.ts +7 -0
  77. package/dist/v2/components/toast-v2.d.ts +57 -0
  78. package/dist/v2/components/tooltip-v2.d.ts +13 -0
  79. package/dist/v2/components/wordmark-v2.d.ts +2 -0
  80. package/package.json +35 -141
  81. package/src/{data-display/accordion → components}/accordion.css +33 -7
  82. package/src/{data-display/accordion → components}/accordion.tsx +14 -14
  83. package/src/components/animated-number.css +11 -29
  84. package/src/components/animated-number.tsx +2 -2
  85. package/src/components/avatar.css +49 -0
  86. package/src/components/avatar.tsx +55 -0
  87. package/src/components/button.css +194 -0
  88. package/src/{actions/button → components}/button.tsx +11 -19
  89. package/src/components/card.css +23 -2
  90. package/src/components/card.tsx +4 -4
  91. package/src/{forms/checkbox → components}/checkbox.css +7 -1
  92. package/src/{forms/checkbox → components}/checkbox.tsx +13 -13
  93. package/src/components/collapsible.tsx +2 -2
  94. package/src/components/dialog.css +181 -0
  95. package/src/components/dialog.tsx +72 -0
  96. package/src/{data-display/diff-changes → components}/diff-changes.css +19 -22
  97. package/src/components/diff-changes.tsx +115 -0
  98. package/src/components/dock-surface.css +9 -2
  99. package/src/components/dropdown-menu.css +135 -0
  100. package/src/components/dropdown-menu.tsx +308 -0
  101. package/src/components/icon-button.css +181 -0
  102. package/src/components/icon-button.tsx +29 -0
  103. package/src/components/icon.css +39 -0
  104. package/src/{icons/icon/additional-icons.ts → components/icon.tsx} +74 -8
  105. package/src/components/image-preview.tsx +2 -3
  106. package/src/components/keybind.css +18 -0
  107. package/src/components/keybind.tsx +20 -0
  108. package/src/components/list.css +5 -5
  109. package/src/components/list.tsx +5 -7
  110. package/src/components/popover.tsx +2 -3
  111. package/src/components/progress-circle.css +16 -0
  112. package/src/components/progress-circle.tsx +64 -0
  113. package/src/components/radio-group.css +187 -0
  114. package/src/components/radio-group.tsx +83 -0
  115. package/src/components/scroll-view.css +4 -36
  116. package/src/components/scroll-view.tsx +88 -153
  117. package/src/components/select.css +201 -0
  118. package/src/components/select.tsx +174 -0
  119. package/src/components/sticky-accordion-header.tsx +1 -1
  120. package/src/components/switch.css +136 -0
  121. package/src/components/switch.tsx +29 -0
  122. package/src/{navigation/tabs → components}/tabs.css +232 -53
  123. package/src/components/tabs.tsx +126 -0
  124. package/src/components/tag.css +37 -0
  125. package/src/components/tag.tsx +22 -0
  126. package/src/components/text-field.tsx +4 -12
  127. package/src/{typography/text-shimmer → components}/text-shimmer.css +1 -2
  128. package/src/{typography/text-shimmer → components}/text-shimmer.tsx +0 -1
  129. package/src/components/toast.css +236 -0
  130. package/src/components/toast.tsx +185 -0
  131. package/src/components/tooltip.css +74 -0
  132. package/src/components/tooltip.tsx +163 -0
  133. package/src/context/dialog.tsx +0 -1
  134. package/src/context/i18n.tsx +14 -17
  135. package/src/context/marked-parser.tsx +13 -3
  136. package/src/hooks/create-auto-scroll.tsx +0 -20
  137. package/src/i18n/am.ts +9 -7
  138. package/src/i18n/ar.ts +9 -7
  139. package/src/i18n/az.ts +9 -7
  140. package/src/i18n/bg.ts +9 -7
  141. package/src/i18n/bn.ts +9 -7
  142. package/src/i18n/br.ts +9 -7
  143. package/src/i18n/bs.ts +9 -7
  144. package/src/i18n/ca.ts +9 -7
  145. package/src/i18n/cs.ts +9 -7
  146. package/src/i18n/da.ts +9 -7
  147. package/src/i18n/de.ts +3 -1
  148. package/src/i18n/dv.ts +9 -7
  149. package/src/i18n/dz.ts +7 -5
  150. package/src/i18n/el.ts +9 -7
  151. package/src/i18n/en.ts +12 -59
  152. package/src/i18n/es.ts +8 -6
  153. package/src/i18n/et.ts +9 -7
  154. package/src/i18n/fa.ts +9 -7
  155. package/src/i18n/fi.ts +9 -7
  156. package/src/i18n/fo.ts +9 -7
  157. package/src/i18n/fr.ts +8 -6
  158. package/src/i18n/hi.ts +9 -7
  159. package/src/i18n/hr.ts +9 -7
  160. package/src/i18n/hu.ts +9 -7
  161. package/src/i18n/hy.ts +9 -7
  162. package/src/i18n/id.ts +9 -7
  163. package/src/i18n/is.ts +9 -7
  164. package/src/i18n/it.ts +9 -7
  165. package/src/i18n/ja.ts +9 -7
  166. package/src/i18n/ka.ts +9 -7
  167. package/src/i18n/km.ts +9 -7
  168. package/src/i18n/ko.ts +9 -7
  169. package/src/i18n/lo.ts +9 -7
  170. package/src/i18n/lt.ts +9 -7
  171. package/src/i18n/lv.ts +9 -7
  172. package/src/i18n/mk.ts +9 -7
  173. package/src/i18n/mn.ts +9 -7
  174. package/src/i18n/ms.ts +9 -7
  175. package/src/i18n/my.ts +9 -7
  176. package/src/i18n/ne.ts +9 -7
  177. package/src/i18n/nl.ts +9 -7
  178. package/src/i18n/no.ts +9 -7
  179. package/src/i18n/pa.ts +9 -7
  180. package/src/i18n/pl.ts +9 -7
  181. package/src/i18n/ro.ts +9 -7
  182. package/src/i18n/ru.ts +9 -7
  183. package/src/i18n/si.ts +9 -7
  184. package/src/i18n/sk.ts +9 -7
  185. package/src/i18n/sl.ts +9 -7
  186. package/src/i18n/sq.ts +9 -7
  187. package/src/i18n/sr.ts +9 -7
  188. package/src/i18n/sv.ts +9 -7
  189. package/src/i18n/tg.ts +9 -7
  190. package/src/i18n/th.ts +9 -7
  191. package/src/i18n/tk.ts +9 -7
  192. package/src/i18n/tr.ts +9 -7
  193. package/src/i18n/uk.ts +9 -7
  194. package/src/i18n/ur.ts +9 -7
  195. package/src/i18n/uz.ts +9 -7
  196. package/src/i18n/vi.ts +9 -7
  197. package/src/i18n/zh.ts +9 -7
  198. package/src/i18n/zht.ts +9 -7
  199. package/src/styles/base.css +0 -5
  200. package/src/styles/index.css +20 -4
  201. package/src/styles/tailwind/colors.css +0 -2
  202. package/src/styles/tailwind/index.css +1 -18
  203. package/src/styles/theme.css +2 -2
  204. package/src/theme/context.tsx +11 -19
  205. package/src/theme/themes/oc-2.json +19 -50
  206. package/src/theme/v2/default-primitives.ts +8 -8
  207. package/src/theme/v2/foreground.ts +0 -1
  208. package/src/theme/v2/mapping.ts +1 -7
  209. package/src/v2/components/accordion-v2.css +139 -0
  210. package/src/v2/components/accordion-v2.tsx +86 -0
  211. package/src/{data-display/avatar/avatar.tsx → v2/components/avatar-v2.tsx} +1 -1
  212. package/src/{data-display/badge/badge.css → v2/components/badge-v2.css} +0 -19
  213. package/src/{data-display/badge/badge.tsx → v2/components/badge-v2.tsx} +4 -6
  214. package/src/{actions/button/button.css → v2/components/button-v2.css} +0 -30
  215. package/src/v2/components/button-v2.tsx +35 -0
  216. package/src/v2/components/checkbox-v2.css +184 -0
  217. package/src/v2/components/checkbox-v2.tsx +65 -0
  218. package/src/{overlays/dialog/dialog.css → v2/components/dialog-v2.css} +0 -3
  219. package/src/{overlays/dialog/dialog.tsx → v2/components/dialog-v2.tsx} +13 -11
  220. package/src/v2/components/diff-changes-v2.css +26 -0
  221. package/src/{data-display/diff-changes/diff-changes.tsx → v2/components/diff-changes-v2.tsx} +2 -7
  222. package/src/{layout/divider/divider.tsx → v2/components/divider-v2.tsx} +3 -3
  223. package/src/{forms/field/field.css → v2/components/field-v2.css} +1 -1
  224. package/src/{forms/field/field.tsx → v2/components/field-v2.tsx} +11 -9
  225. package/src/{styles/file-tree.css → v2/components/file-tree-v2.css} +3 -64
  226. package/src/{actions/icon-button/icon-button.css → v2/components/icon-button-v2.css} +9 -27
  227. package/src/v2/components/icon-button-v2.tsx +37 -0
  228. package/src/{icons/icon → v2/components}/icon.tsx +12 -88
  229. package/src/v2/components/inline-input-v2.css +225 -0
  230. package/src/v2/components/inline-input-v2.tsx +107 -0
  231. package/src/{data-display/keybind/keybind.css → v2/components/keybind-v2.css} +1 -1
  232. package/src/{data-display/keybind/keybind.tsx → v2/components/keybind-v2.tsx} +3 -3
  233. package/src/{data-display/line-comment/line-comment.css → v2/components/line-comment-v2.css} +4 -4
  234. package/src/{data-display/line-comment/line-comment.tsx → v2/components/line-comment-v2.tsx} +15 -17
  235. package/src/{feedback/loader/loader.tsx → v2/components/loader-v2.tsx} +2 -2
  236. package/src/{navigation/menu/menu.css → v2/components/menu-v2.css} +2 -124
  237. package/src/{navigation/menu/menu.tsx → v2/components/menu-v2.tsx} +32 -96
  238. package/src/v2/components/progress-circle-v2.css +13 -0
  239. package/src/v2/components/progress-circle-v2.tsx +52 -0
  240. package/src/{data-display/project-avatar/project-avatar.css → v2/components/project-avatar-v2.css} +4 -4
  241. package/src/{data-display/project-avatar/project-avatar.tsx → v2/components/project-avatar-v2.tsx} +1 -1
  242. package/src/{forms/radio/radio.tsx → v2/components/radio-v2.tsx} +20 -30
  243. package/src/{navigation/segmented-control/segmented-control.css → v2/components/segmented-control-v2.css} +1 -1
  244. package/src/{navigation/segmented-control/segmented-control.tsx → v2/components/segmented-control-v2.tsx} +8 -9
  245. package/src/{forms/select/select.css → v2/components/select-v2.css} +8 -2
  246. package/src/{forms/select/select.tsx → v2/components/select-v2.tsx} +32 -42
  247. package/src/{actions/split-button/split-button.css → v2/components/split-button-v2.css} +6 -2
  248. package/src/{actions/split-button/split-button.tsx → v2/components/split-button-v2.tsx} +4 -4
  249. package/src/{forms/switch/switch.css → v2/components/switch-v2.css} +0 -110
  250. package/src/v2/components/switch-v2.tsx +28 -0
  251. package/src/{navigation/tabs/tabs-current.css → v2/components/tabs-v2.css} +10 -25
  252. package/src/v2/components/tabs-v2.tsx +149 -0
  253. package/src/{forms/text-input/text-input.css → v2/components/text-input-v2.css} +3 -3
  254. package/src/{forms/text-input/text-input.tsx → v2/components/text-input-v2.tsx} +4 -4
  255. package/src/v2/components/text-shimmer-v2.css +125 -0
  256. package/src/v2/components/text-shimmer-v2.tsx +63 -0
  257. package/src/{forms/textarea/textarea.tsx → v2/components/textarea-v2.tsx} +3 -3
  258. package/src/{feedback/toast/toast.tsx → v2/components/toast-v2.tsx} +58 -60
  259. package/src/{overlays/tooltip/tooltip.css → v2/components/tooltip-v2.css} +0 -33
  260. package/src/{overlays/tooltip/tooltip.tsx → v2/components/tooltip-v2.tsx} +17 -24
  261. package/src/v2/components/wordmark-v2.tsx +71 -0
  262. package/src/{styles/tokens → v2/styles}/colors.css +8 -8
  263. package/src/{styles/tokens → v2/styles}/theme.css +16 -23
  264. package/dist/actions/button/button.d.ts +0 -10
  265. package/dist/actions/icon-button/icon-button.d.ts +0 -11
  266. package/dist/actions/split-button/split-button.d.ts +0 -5
  267. package/dist/context/marked-base.d.ts +0 -3
  268. package/dist/data-display/badge/badge.d.ts +0 -7
  269. package/dist/data-display/keybind/keybind.d.ts +0 -7
  270. package/dist/feedback/loader/loader.d.ts +0 -3
  271. package/dist/feedback/toast/toast.d.ts +0 -57
  272. package/dist/forms/radio/radio.d.ts +0 -16
  273. package/dist/forms/textarea/textarea.d.ts +0 -7
  274. package/dist/i18n/he.d.ts +0 -197
  275. package/dist/layout/divider/divider.d.ts +0 -5
  276. package/dist/navigation/menu/menu.d.ts +0 -59
  277. package/dist/overlays/tooltip/tooltip.d.ts +0 -15
  278. package/dist/typography/wordmark/wordmark.d.ts +0 -5
  279. package/src/actions/icon-button/icon-button.tsx +0 -32
  280. package/src/context/marked-base.tsx +0 -27
  281. package/src/feedback/progress-circle/progress-circle.css +0 -28
  282. package/src/feedback/progress-circle/progress-circle.tsx +0 -64
  283. package/src/forms/switch/switch.tsx +0 -34
  284. package/src/i18n/he.ts +0 -199
  285. package/src/icons/icon/icon.css +0 -3
  286. package/src/navigation/tabs/tabs.tsx +0 -194
  287. package/src/typography/wordmark/wordmark.tsx +0 -39
  288. /package/dist/{forms/inline-input → components}/inline-input.d.ts +0 -0
  289. /package/dist/{navigation → v2/components}/tab-state-indicator.d.ts +0 -0
  290. /package/src/{forms/inline-input → components}/inline-input.css +0 -0
  291. /package/src/{forms/inline-input → components}/inline-input.tsx +0 -0
  292. /package/src/{data-display/avatar/avatar.css → v2/components/avatar-v2.css} +0 -0
  293. /package/src/{layout/divider/divider.css → v2/components/divider-v2.css} +0 -0
  294. /package/src/{feedback/loader/loader.css → v2/components/loader-v2.css} +0 -0
  295. /package/src/{forms/radio/radio.css → v2/components/radio-v2.css} +0 -0
  296. /package/src/{navigation → v2/components}/tab-state-indicator.tsx +0 -0
  297. /package/src/{forms/textarea/textarea.css → v2/components/textarea-v2.css} +0 -0
  298. /package/src/{feedback/toast/toast.css → v2/components/toast-v2.css} +0 -0
  299. /package/src/{styles/tokens/index.css → v2/styles/tailwind.css} +0 -0
@@ -0,0 +1,181 @@
1
+ [data-component="icon-button"] {
2
+ display: inline-flex;
3
+ align-items: center;
4
+ justify-content: center;
5
+ border-radius: var(--radius-sm);
6
+ text-decoration: none;
7
+ user-select: none;
8
+ aspect-ratio: 1;
9
+ flex-shrink: 0;
10
+
11
+ &[data-variant="primary"] {
12
+ background-color: var(--icon-strong-base);
13
+
14
+ [data-slot="icon-svg"] {
15
+ /* color: var(--icon-weak-base); */
16
+ color: var(--icon-invert-base);
17
+
18
+ /* &:hover:not(:disabled) { */
19
+ /* color: var(--icon-weak-hover); */
20
+ /* } */
21
+ /* &:active:not(:disabled) { */
22
+ /* color: var(--icon-strong-active); */
23
+ /* } */
24
+ }
25
+
26
+ &:hover:not(:disabled) {
27
+ background-color: var(--icon-strong-hover);
28
+ }
29
+ &:focus:not(:disabled) {
30
+ background-color: var(--icon-strong-focus);
31
+ }
32
+ &:active:not(:disabled) {
33
+ background-color: var(--icon-strong-active);
34
+ }
35
+ &:disabled {
36
+ background-color: var(--icon-strong-disabled);
37
+
38
+ [data-slot="icon-svg"] {
39
+ color: var(--icon-invert-base);
40
+ }
41
+ }
42
+ }
43
+
44
+ &[data-variant="secondary"] {
45
+ border: transparent;
46
+ background-color: var(--button-secondary-base);
47
+ color: var(--text-strong);
48
+ box-shadow: var(--shadow-xs-border-base);
49
+
50
+ &:hover:not(:disabled) {
51
+ background-color: var(--button-secondary-hover);
52
+ }
53
+ &:focus:not(:disabled) {
54
+ background-color: var(--button-secondary-base);
55
+ }
56
+ &:focus-visible:not(:active) {
57
+ background-color: var(--button-secondary-base);
58
+ box-shadow: var(--shadow-xs-border-focus);
59
+ }
60
+ &:focus-visible:active {
61
+ box-shadow: none;
62
+ }
63
+ &:active:not(:disabled) {
64
+ background-color: var(--button-secondary-base);
65
+ }
66
+
67
+ [data-slot="icon-svg"] {
68
+ color: var(--icon-strong-base);
69
+ }
70
+
71
+ &:disabled {
72
+ background-color: var(--icon-strong-disabled);
73
+ color: var(--icon-invert-base);
74
+ cursor: not-allowed;
75
+ }
76
+ }
77
+
78
+ &[data-variant="ghost"] {
79
+ background-color: transparent;
80
+ /* color: var(--icon-base); */
81
+
82
+ [data-slot="icon-svg"] {
83
+ color: var(--icon-base);
84
+ }
85
+
86
+ &:hover:not(:disabled) {
87
+ background-color: var(--surface-base-hover);
88
+
89
+ /* [data-slot="icon-svg"] { */
90
+ /* color: var(--icon-hover); */
91
+ /* } */
92
+ }
93
+ &:focus-visible:not(:disabled) {
94
+ background-color: var(--surface-base-hover);
95
+ }
96
+ &:active:not(:disabled) {
97
+ background-color: var(--surface-base-active);
98
+ /* [data-slot="icon-svg"] { */
99
+ /* color: var(--icon-active); */
100
+ /* } */
101
+ }
102
+ &:selected:not(:disabled) {
103
+ background-color: var(--surface-base-active);
104
+ /* [data-slot="icon-svg"] { */
105
+ /* color: var(--icon-selected); */
106
+ /* } */
107
+ }
108
+ &:disabled {
109
+ color: var(--icon-invert-base);
110
+ cursor: not-allowed;
111
+ }
112
+ }
113
+
114
+ &[data-size="normal"] {
115
+ width: 24px;
116
+ height: 24px;
117
+
118
+ font-size: var(--font-size-small);
119
+ line-height: var(--line-height-large);
120
+ gap: calc(var(--spacing) * 0.5);
121
+ }
122
+
123
+ &[data-size="small"] {
124
+ width: 20px;
125
+ height: 20px;
126
+ }
127
+
128
+ &[data-size="large"] {
129
+ height: 32px;
130
+ /* padding: 0 8px 0 6px; */
131
+ gap: 8px;
132
+
133
+ /* text-12-medium */
134
+ font-family: var(--font-family-sans);
135
+ font-size: var(--font-size-small);
136
+ font-style: normal;
137
+ font-weight: var(--font-weight-medium);
138
+ line-height: var(--line-height-large); /* 166.667% */
139
+ letter-spacing: var(--letter-spacing-normal);
140
+ }
141
+
142
+ &:focus {
143
+ outline: none;
144
+ }
145
+ }
146
+
147
+ @media (prefers-reduced-motion: no-preference) {
148
+ [data-component="icon-button"][data-icon="stop"] [data-slot="icon-svg"] rect {
149
+ transform-origin: center;
150
+ transform-box: fill-box;
151
+ animation: stop-pulse 1.8s ease-in-out infinite;
152
+ }
153
+ }
154
+
155
+ @keyframes stop-pulse {
156
+ 0%,
157
+ 100% {
158
+ transform: scale(0.95);
159
+ }
160
+ 50% {
161
+ transform: scale(1.12);
162
+ }
163
+ }
164
+
165
+ [data-component="icon-button"].titlebar-icon {
166
+ width: 32px;
167
+ height: 24px;
168
+ aspect-ratio: auto;
169
+ }
170
+
171
+ [data-component="icon-button"].titlebar-icon[data-variant="ghost"][aria-expanded="true"] {
172
+ background-color: var(--surface-raised-base-active);
173
+ }
174
+
175
+ [data-component="icon-button"].titlebar-icon[data-variant="ghost"][aria-expanded="true"] [data-slot="icon-svg"] {
176
+ color: var(--icon-strong-base);
177
+ }
178
+
179
+ [data-component="icon-button"].titlebar-icon[data-variant="ghost"][aria-expanded="true"]:hover:not(:disabled) {
180
+ background-color: var(--surface-raised-base-active);
181
+ }
@@ -0,0 +1,29 @@
1
+ import { Button as Kobalte } from "@kobalte/core/button"
2
+ import { type ComponentProps, splitProps } from "solid-js"
3
+ import { Icon, IconProps } from "./icon"
4
+
5
+ export interface IconButtonProps extends ComponentProps<typeof Kobalte> {
6
+ icon: IconProps["name"]
7
+ size?: "small" | "normal" | "large"
8
+ iconSize?: IconProps["size"]
9
+ variant?: "primary" | "secondary" | "ghost"
10
+ }
11
+
12
+ export function IconButton(props: ComponentProps<"button"> & IconButtonProps) {
13
+ const [split, rest] = splitProps(props, ["variant", "size", "iconSize", "class", "classList"])
14
+ return (
15
+ <Kobalte
16
+ {...rest}
17
+ data-component="icon-button"
18
+ data-icon={props.icon}
19
+ data-size={split.size || "normal"}
20
+ data-variant={split.variant || "secondary"}
21
+ classList={{
22
+ ...split.classList,
23
+ [split.class ?? ""]: !!split.class,
24
+ }}
25
+ >
26
+ <Icon name={props.icon} size={split.iconSize ?? (split.size === "large" ? "normal" : "small")} />
27
+ </Kobalte>
28
+ )
29
+ }
@@ -0,0 +1,39 @@
1
+ [data-component="icon"] {
2
+ display: inline-flex;
3
+ align-items: center;
4
+ justify-content: center;
5
+ flex-shrink: 0;
6
+ /* resize: both; */
7
+ aspect-ratio: 1/1;
8
+ color: var(--icon-base);
9
+
10
+ &[data-size="small"] {
11
+ width: 16px;
12
+ height: 16px;
13
+ }
14
+
15
+ &[data-size="normal"] {
16
+ width: 20px;
17
+ height: 20px;
18
+ }
19
+
20
+ &[data-size="medium"] {
21
+ width: 24px;
22
+ height: 24px;
23
+ }
24
+
25
+ &[data-size="large"] {
26
+ width: 24px;
27
+ height: 24px;
28
+ }
29
+
30
+ [data-slot="icon-svg"] {
31
+ width: 100%;
32
+ height: auto;
33
+ }
34
+ }
35
+
36
+ :dir(rtl) [data-component="icon"][data-directional] [data-slot="icon-svg"],
37
+ :dir(rtl) [data-slot="menu-v2-item-chevron"] {
38
+ transform: scaleX(-1);
39
+ }
@@ -1,4 +1,6 @@
1
- export const additionalIcons = {
1
+ import { onMount, splitProps, type ComponentProps } from "solid-js"
2
+
3
+ const icons = {
2
4
  "align-right": `<path d="M12.292 6.04167L16.2503 9.99998L12.292 13.9583M2.91699 9.99998H15.6253M17.0837 3.75V16.25" stroke="currentColor" stroke-linecap="square"/>`,
3
5
  "arrow-up": `<path fill-rule="evenodd" clip-rule="evenodd" d="M9.99991 2.24121L16.0921 8.33343L15.2083 9.21731L10.6249 4.63397V17.5001H9.37492V4.63398L4.7916 9.21731L3.90771 8.33343L9.99991 2.24121Z" fill="currentColor"/>`,
4
6
  "arrow-left": `<path d="M8.33464 4.58398L2.91797 10.0007L8.33464 15.4173M3.33464 10.0007H17.0846" stroke="currentColor" stroke-linecap="square"/>`,
@@ -8,7 +10,6 @@ export const additionalIcons = {
8
10
  prompt: `<path d="M14.5841 12.0807H17.9193V2.91406H5.6276V6.2474M14.5859 6.2474H2.08594V15.4141H5.0026V17.4974L8.7526 15.4141H14.5859V6.2474Z" stroke="currentColor" stroke-linecap="square"/>`,
9
11
  brain: `<path d="M13.332 8.7487C11.4911 8.7487 9.9987 7.25631 9.9987 5.41536M6.66536 11.2487C8.50631 11.2487 9.9987 12.7411 9.9987 14.582M9.9987 2.78209L9.9987 17.0658M16.004 15.0475C17.1255 14.5876 17.9154 13.4849 17.9154 12.1978C17.9154 11.3363 17.5615 10.5575 16.9913 9.9987C17.5615 9.43991 17.9154 8.66108 17.9154 7.79962C17.9154 6.21199 16.7136 4.90504 15.1702 4.73878C14.7858 3.21216 13.4039 2.08203 11.758 2.08203C11.1171 2.08203 10.5162 2.25337 9.9987 2.55275C9.48117 2.25337 8.88032 2.08203 8.23944 2.08203C6.59353 2.08203 5.21157 3.21216 4.82722 4.73878C3.28377 4.90504 2.08203 6.21199 2.08203 7.79962C2.08203 8.66108 2.43585 9.43991 3.00609 9.9987C2.43585 10.5575 2.08203 11.3363 2.08203 12.1978C2.08203 13.4849 2.87191 14.5876 3.99339 15.0475C4.46688 16.7033 5.9917 17.9154 7.79962 17.9154C8.61335 17.9154 9.36972 17.6698 9.9987 17.2488C10.6277 17.6698 11.384 17.9154 12.1978 17.9154C14.0057 17.9154 15.5305 16.7033 16.004 15.0475Z" stroke="currentColor"/>`,
10
12
  fork: `<path d="M2.91602 7.91406L2.91602 2.91406H7.91602M12.0827 2.91406H17.0827L17.0827 7.91406M9.99935 9.9974L9.99935 17.0807M9.99935 9.9974L3.33268 3.33073M9.99935 9.9974L16.666 3.33073" stroke="currentColor" stroke-linecap="square"/>`,
11
- "workspace-isolated": `<g transform="translate(2 2)"><path d="M10.5 10.5V5.5H5.5V10.5H10.5Z" fill="currentColor"/><rect x="2.5" y="2.5" width="11" height="11" stroke="currentColor"/></g>`,
12
13
  "bullet-list": `<path d="M9.58329 13.7497H17.0833M9.58329 6.24967H17.0833M6.24996 6.24967C6.24996 7.17015 5.50377 7.91634 4.58329 7.91634C3.66282 7.91634 2.91663 7.17015 2.91663 6.24967C2.91663 5.3292 3.66282 4.58301 4.58329 4.58301C5.50377 4.58301 6.24996 5.3292 6.24996 6.24967ZM6.24996 13.7497C6.24996 14.6701 5.50377 15.4163 4.58329 15.4163C3.66282 15.4163 2.91663 14.6701 2.91663 13.7497C2.91663 12.8292 3.66282 12.083 4.58329 12.083C5.50377 12.083 6.24996 12.8292 6.24996 13.7497Z" stroke="currentColor" stroke-linecap="square"/>`,
13
14
  "check-small": `<path d="M6.5 11.4412L8.97059 13.5L13.5 6.5" stroke="currentColor" stroke-linecap="square"/>`,
14
15
  "chevron-down": `<path d="M6.6665 8.33325L9.99984 11.6666L13.3332 8.33325" stroke="currentColor" stroke-linecap="square"/>`,
@@ -102,14 +103,79 @@ export const additionalIcons = {
102
103
  link: `<path d="M2.08334 12.0833L1.72979 11.7298L1.37624 12.0833L1.72979 12.4369L2.08334 12.0833ZM7.91668 17.9167L7.56312 18.2702L7.91668 18.6238L8.27023 18.2702L7.91668 17.9167ZM17.9167 7.91666L18.2702 8.27022L18.6238 7.91666L18.2702 7.56311L17.9167 7.91666ZM12.0833 2.08333L12.4369 1.72977L12.0833 1.37622L11.7298 1.72977L12.0833 2.08333ZM8.39646 5.06311L8.0429 5.41666L8.75001 6.12377L9.10356 5.77021L8.75001 5.41666L8.39646 5.06311ZM5.77023 9.10355L6.12378 8.74999L5.41668 8.04289L5.06312 8.39644L5.41668 8.74999L5.77023 9.10355ZM14.2298 10.8964L13.8762 11.25L14.5833 11.9571L14.9369 11.6035L14.5833 11.25L14.2298 10.8964ZM11.6036 14.9369L11.9571 14.5833L11.25 13.8762L10.8965 14.2298L11.25 14.5833L11.6036 14.9369ZM7.14646 12.1464L6.7929 12.5L7.50001 13.2071L7.85356 12.8535L7.50001 12.5L7.14646 12.1464ZM12.8536 7.85355L13.2071 7.49999L12.5 6.79289L12.1465 7.14644L12.5 7.49999L12.8536 7.85355ZM2.08334 12.0833L1.72979 12.4369L7.56312 18.2702L7.91668 17.9167L8.27023 17.5631L2.4369 11.7298L2.08334 12.0833ZM17.9167 7.91666L18.2702 7.56311L12.4369 1.72977L12.0833 2.08333L11.7298 2.43688L17.5631 8.27022L17.9167 7.91666ZM12.0833 2.08333L11.7298 1.72977L8.39646 5.06311L8.75001 5.41666L9.10356 5.77021L12.4369 2.43688L12.0833 2.08333ZM5.41668 8.74999L5.06312 8.39644L1.72979 11.7298L2.08334 12.0833L2.4369 12.4369L5.77023 9.10355L5.41668 8.74999ZM14.5833 11.25L14.9369 11.6035L18.2702 8.27022L17.9167 7.91666L17.5631 7.56311L14.2298 10.8964L14.5833 11.25ZM7.91668 17.9167L8.27023 18.2702L11.6036 14.9369L11.25 14.5833L10.8965 14.2298L7.56312 17.5631L7.91668 17.9167ZM7.50001 12.5L7.85356 12.8535L12.8536 7.85355L12.5 7.49999L12.1465 7.14644L7.14646 12.1464L7.50001 12.5Z" fill="currentColor"/>`,
103
104
  providers: `<path d="M10.0001 4.37562V2.875M13 4.37793V2.87793M7.00014 4.37793V2.875M10 17.1279V15.6279M13 17.1279V15.6279M7 17.1279V15.6279M15.625 13.0029H17.125M15.625 7.00293H17.125M15.625 10.0029H17.125M2.875 10.0029H4.375M2.875 13.0029H4.375M2.875 7.00293H4.375M4.375 4.37793H15.625V15.6279H4.375V4.37793ZM12.6241 10.0022C12.6241 11.4519 11.4488 12.6272 9.99908 12.6272C8.54934 12.6272 7.37408 11.4519 7.37408 10.0022C7.37408 8.55245 8.54934 7.3772 9.99908 7.3772C11.4488 7.3772 12.6241 8.55245 12.6241 10.0022Z" stroke="currentColor" stroke-linecap="square"/>`,
104
105
  models: `<path fill-rule="evenodd" clip-rule="evenodd" d="M17.5 10C12.2917 10 10 12.2917 10 17.5C10 12.2917 7.70833 10 2.5 10C7.70833 10 10 7.70833 10 2.5C10 7.70833 12.2917 10 17.5 10Z" stroke="currentColor"/>`,
105
- appearance: `<path d="M2.707 14.707L14.707 2.707M2.707 9.06L9.06 2.707M2.707 3.413L3.413 2.707M8.354 14.707L14.707 8.354M14.000 14.706L14.706 14.000" stroke="currentColor" stroke-linecap="square"/>`,
106
- notifications: `<path d="M15.389 7.278V9.611C15.389 10.593 15.389 11.389 15.389 11.389H11.833M6.056 11.389H2.5C2.5 11.389 2.5 10.593 2.5 9.611V4.278C2.5 3.296 2.5 2.5 2.5 2.5H9.945M6.056 11.389V13.611H8.944H11.833V11.389M6.056 11.389H11.833" stroke="currentColor"/><circle cx="14.5" cy="4.5" r="2" stroke="currentColor" fill="currentColor"/>`,
107
- extensions: `<path d="M9.166 6.805V9.305M11.834 6.805V9.305M6.5 6.805V9.305M2.5 2.5V13.61H15.833V2.5H2.5Z" stroke="currentColor" stroke-linecap="square"/>`,
108
- cube: `<path d="M10 2.5L16.5 6.25V13.75L10 17.5L3.5 13.75V6.25L10 2.5Z" stroke="currentColor"/><path d="M10 10L16.5 6.25M10 10V17.5M10 10L3.5 6.25" stroke="currentColor"/>`,
109
- "post-skill": `<rect x="2.5" y="3.5" width="15" height="13" rx="1.5" stroke="currentColor"/><path d="M5.5 7.5H10.5M5.5 10.5H14.5" stroke="currentColor"/>`,
110
106
  "arrow-undo-down": `<path d="M4.08333 11.0859L1.75 8.7526L4.08333 6.41927M2.33333 8.7526L12.5417 8.7526L12.5417 3.21094L7 3.21094" stroke="currentColor" stroke-width="1" stroke-linecap="square"/>`,
111
107
  }
112
108
 
113
- export function additionalIconViewBox(name: keyof typeof additionalIcons) {
109
+ const spriteID = "opencode-icon-sprite"
110
+ const symbol = (name: keyof typeof icons) => `opencode-icon-${name}`
111
+ let spriteInserted = false
112
+
113
+ function viewBox(name: keyof typeof icons) {
114
114
  return name === "magnifying-glass" || name === "arrow-undo-down" || name === "subagent" ? "0 0 16 16" : "0 0 20 20"
115
115
  }
116
+
117
+ function ensureSprite() {
118
+ if (spriteInserted) return
119
+ if (typeof document === "undefined") return
120
+ if (document.getElementById(spriteID)) {
121
+ spriteInserted = true
122
+ return
123
+ }
124
+ const body = document.body as HTMLElement | null
125
+ if (!body) return
126
+
127
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg")
128
+ svg.id = spriteID
129
+ svg.setAttribute("aria-hidden", "true")
130
+ svg.setAttribute("width", "0")
131
+ svg.setAttribute("height", "0")
132
+ svg.style.position = "absolute"
133
+ svg.style.overflow = "hidden"
134
+ svg.innerHTML = Object.entries(icons)
135
+ .map(([name, path]) => {
136
+ const key = name as keyof typeof icons
137
+ return `<symbol id="${symbol(key)}" viewBox="${viewBox(key)}">${path}</symbol>`
138
+ })
139
+ .join("")
140
+ body.insertBefore(svg, body.firstChild)
141
+ spriteInserted = true
142
+ }
143
+
144
+ export interface IconProps extends ComponentProps<"svg"> {
145
+ name: keyof typeof icons
146
+ size?: "small" | "normal" | "medium" | "large"
147
+ }
148
+
149
+ export function Icon(props: IconProps) {
150
+ const [local, others] = splitProps(props, ["name", "size", "class", "classList"])
151
+ onMount(ensureSprite)
152
+
153
+ return (
154
+ <div
155
+ data-component="icon"
156
+ data-size={local.size || "normal"}
157
+ data-directional={
158
+ local.name === "arrow-left" ||
159
+ local.name === "arrow-right" ||
160
+ local.name === "chevron-left" ||
161
+ local.name === "chevron-right"
162
+ ? true
163
+ : undefined
164
+ }
165
+ >
166
+ <svg
167
+ data-slot="icon-svg"
168
+ classList={{
169
+ ...local.classList,
170
+ [local.class ?? ""]: !!local.class,
171
+ }}
172
+ fill="none"
173
+ viewBox={viewBox(local.name)}
174
+ aria-hidden="true"
175
+ {...others}
176
+ >
177
+ <use href={`#${symbol(local.name)}`} />
178
+ </svg>
179
+ </div>
180
+ )
181
+ }
@@ -1,7 +1,6 @@
1
1
  import { Dialog as Kobalte } from "@kobalte/core/dialog"
2
2
  import { useI18n } from "../context/i18n"
3
- import { Icon } from "@opencode-ai/ui/icon"
4
- import { IconButton } from "@opencode-ai/ui/icon-button"
3
+ import { IconButton } from "./icon-button"
5
4
 
6
5
  export interface ImagePreviewProps {
7
6
  src: string
@@ -18,7 +17,7 @@ export function ImagePreview(props: ImagePreviewProps) {
18
17
  <Kobalte.CloseButton
19
18
  data-slot="image-preview-close"
20
19
  as={IconButton}
21
- icon={<Icon name="close" />}
20
+ icon="close"
22
21
  variant="ghost"
23
22
  aria-label={i18n.t("ui.common.close")}
24
23
  />
@@ -0,0 +1,18 @@
1
+ [data-component="keybind"] {
2
+ display: flex;
3
+ align-items: center;
4
+ justify-content: center;
5
+ flex-shrink: 0;
6
+ height: 20px;
7
+ padding: 0 8px;
8
+ border-radius: 2px;
9
+ background: var(--surface-base);
10
+ box-shadow: var(--shadow-xxs-border);
11
+
12
+ /* text-12-regular */
13
+ font-family: var(--font-family-sans);
14
+ font-size: 12px;
15
+ font-weight: var(--font-weight-regular);
16
+ line-height: 1;
17
+ color: var(--text-weak);
18
+ }
@@ -0,0 +1,20 @@
1
+ import type { ComponentProps, ParentProps } from "solid-js"
2
+
3
+ export interface KeybindProps extends ParentProps {
4
+ class?: string
5
+ classList?: ComponentProps<"span">["classList"]
6
+ }
7
+
8
+ export function Keybind(props: KeybindProps) {
9
+ return (
10
+ <span
11
+ data-component="keybind"
12
+ classList={{
13
+ ...props.classList,
14
+ [props.class ?? ""]: !!props.class,
15
+ }}
16
+ >
17
+ {props.children}
18
+ </span>
19
+ )
20
+ }
@@ -31,7 +31,7 @@
31
31
  align-self: stretch;
32
32
  margin-bottom: 4px;
33
33
 
34
- > [data-component="icon-button-v2"] {
34
+ > [data-component="icon-button"] {
35
35
  width: 24px;
36
36
  height: 24px;
37
37
  flex-shrink: 0;
@@ -83,7 +83,7 @@
83
83
  }
84
84
  }
85
85
 
86
- > [data-component="icon-button-v2"] {
86
+ > [data-component="icon-button"] {
87
87
  width: 20px;
88
88
  height: 20px;
89
89
  background-color: transparent;
@@ -106,7 +106,7 @@
106
106
  }
107
107
  }
108
108
 
109
- > [data-component="icon-button-v2"] {
109
+ > [data-component="icon-button"] {
110
110
  background-color: transparent;
111
111
 
112
112
  &:hover:not(:disabled),
@@ -252,7 +252,7 @@
252
252
  justify-content: center;
253
253
  flex-shrink: 0;
254
254
  aspect-ratio: 1/1;
255
- [data-slot="icon-svg"] {
255
+ [data-component="icon"] {
256
256
  color: var(--icon-strong-base);
257
257
  }
258
258
  }
@@ -262,7 +262,7 @@
262
262
  justify-content: center;
263
263
  flex-shrink: 0;
264
264
  aspect-ratio: 1/1;
265
- [data-slot="icon-svg"] {
265
+ [data-component="icon"] {
266
266
  color: var(--icon-strong-base);
267
267
  }
268
268
  }
@@ -3,8 +3,8 @@ import { createEffect, For, type JSX, on, Show } from "solid-js"
3
3
  import { createStore } from "solid-js/store"
4
4
  import { makeEventListener } from "@solid-primitives/event-listener"
5
5
  import { useI18n } from "../context/i18n"
6
- import { Icon, type IconProps } from "@opencode-ai/ui/icon"
7
- import { IconButton } from "@opencode-ai/ui/icon-button"
6
+ import { Icon, type IconProps } from "./icon"
7
+ import { IconButton } from "./icon-button"
8
8
  import { TextField } from "./text-field"
9
9
 
10
10
  function findByKey(container: HTMLElement, key: string) {
@@ -302,7 +302,7 @@ export function List<T>(props: ListProps<T> & { ref?: (ref: ListRef) => void })
302
302
  </div>
303
303
  <Show when={internalFilter()}>
304
304
  <IconButton
305
- icon={<Icon name="circle-x" />}
305
+ icon="circle-x"
306
306
  variant="ghost"
307
307
  onClick={() => {
308
308
  setInternalFilter("")
@@ -367,11 +367,9 @@ export function List<T>(props: ListProps<T> & { ref?: (ref: ListRef) => void })
367
367
  </span>
368
368
  )}
369
369
  </Show>
370
- <Show
371
- when={props.divider && (i() !== group.items.length - 1 || (showAdd() && isLastGroup()))}
372
- >
370
+ {props.divider && (i() !== group.items.length - 1 || (showAdd() && isLastGroup())) && (
373
371
  <span data-slot="list-item-divider" />
374
- </Show>
372
+ )}
375
373
  </button>
376
374
  )
377
375
  if (props.itemWrapper) return props.itemWrapper(item, node)
@@ -3,8 +3,7 @@ import { ComponentProps, JSXElement, ParentProps, Show, createEffect, splitProps
3
3
  import { createStore } from "solid-js/store"
4
4
  import { makeEventListener } from "@solid-primitives/event-listener"
5
5
  import { useI18n } from "../context/i18n"
6
- import { Icon } from "@opencode-ai/ui/icon"
7
- import { IconButton } from "@opencode-ai/ui/icon-button"
6
+ import { IconButton } from "./icon-button"
8
7
 
9
8
  export interface PopoverProps<T extends ValidComponent = "div">
10
9
  extends ParentProps,
@@ -123,7 +122,7 @@ export function Popover<T extends ValidComponent = "div">(props: PopoverProps<T>
123
122
  <Kobalte.CloseButton
124
123
  data-slot="popover-close-button"
125
124
  as={IconButton}
126
- icon={<Icon name="close" />}
125
+ icon="close"
127
126
  variant="ghost"
128
127
  aria-label={i18n.t("ui.common.close")}
129
128
  />
@@ -0,0 +1,16 @@
1
+ [data-component="progress-circle"] {
2
+ transform: rotate(-90deg);
3
+
4
+ [data-slot="progress-circle-background"] {
5
+ stroke: var(--progress-circle-background, var(--border-weak-base));
6
+ }
7
+
8
+ [data-slot="progress-circle-background-overlay"] {
9
+ stroke: var(--progress-circle-background-overlay, transparent);
10
+ }
11
+
12
+ [data-slot="progress-circle-progress"] {
13
+ stroke: var(--progress-circle-progress, var(--border-active));
14
+ transition: stroke-dashoffset 0.35s cubic-bezier(0.65, 0, 0.35, 1);
15
+ }
16
+ }
@@ -0,0 +1,64 @@
1
+ import { type ComponentProps, createMemo, splitProps } from "solid-js"
2
+
3
+ export interface ProgressCircleProps extends Pick<ComponentProps<"svg">, "class" | "classList" | "style"> {
4
+ percentage: number
5
+ size?: number
6
+ strokeWidth?: number
7
+ }
8
+
9
+ export function ProgressCircle(props: ProgressCircleProps) {
10
+ const [split, rest] = splitProps(props, ["percentage", "size", "strokeWidth", "class", "classList"])
11
+
12
+ const size = () => split.size || 16
13
+ const strokeWidth = () => split.strokeWidth || 3
14
+
15
+ const viewBoxSize = 16
16
+ const center = viewBoxSize / 2
17
+ const radius = () => center - strokeWidth() / 2
18
+ const circumference = createMemo(() => 2 * Math.PI * radius())
19
+
20
+ const offset = createMemo(() => {
21
+ const clampedPercentage = Math.max(0, Math.min(100, split.percentage || 0))
22
+ const progress = clampedPercentage / 100
23
+ return circumference() * (1 - progress)
24
+ })
25
+
26
+ return (
27
+ <svg
28
+ {...rest}
29
+ width={size()}
30
+ height={size()}
31
+ viewBox={`0 0 ${viewBoxSize} ${viewBoxSize}`}
32
+ fill="none"
33
+ data-component="progress-circle"
34
+ classList={{
35
+ ...split.classList,
36
+ [split.class ?? ""]: !!split.class,
37
+ }}
38
+ >
39
+ <circle
40
+ cx={center}
41
+ cy={center}
42
+ r={radius()}
43
+ data-slot="progress-circle-background"
44
+ stroke-width={strokeWidth()}
45
+ />
46
+ <circle
47
+ cx={center}
48
+ cy={center}
49
+ r={radius()}
50
+ data-slot="progress-circle-background-overlay"
51
+ stroke-width={strokeWidth()}
52
+ />
53
+ <circle
54
+ cx={center}
55
+ cy={center}
56
+ r={radius()}
57
+ data-slot="progress-circle-progress"
58
+ stroke-width={strokeWidth()}
59
+ stroke-dasharray={circumference().toString()}
60
+ stroke-dashoffset={offset()}
61
+ />
62
+ </svg>
63
+ )
64
+ }