llms-py 3.0.0__py3-none-any.whl → 3.0.0b2__py3-none-any.whl

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 (195) hide show
  1. llms/__pycache__/main.cpython-314.pyc +0 -0
  2. llms/index.html +37 -26
  3. llms/llms.json +21 -70
  4. llms/main.py +731 -1426
  5. llms/providers.json +1 -1
  6. llms/{extensions/analytics/ui/index.mjs → ui/Analytics.mjs} +238 -154
  7. llms/ui/App.mjs +63 -133
  8. llms/ui/Avatar.mjs +86 -0
  9. llms/ui/Brand.mjs +52 -0
  10. llms/ui/ChatPrompt.mjs +597 -0
  11. llms/ui/Main.mjs +862 -0
  12. llms/ui/OAuthSignIn.mjs +61 -0
  13. llms/ui/ProviderIcon.mjs +36 -0
  14. llms/ui/ProviderStatus.mjs +104 -0
  15. llms/{extensions/app/ui → ui}/Recents.mjs +57 -82
  16. llms/ui/{modules/chat/SettingsDialog.mjs → SettingsDialog.mjs} +9 -9
  17. llms/{extensions/app/ui/index.mjs → ui/Sidebar.mjs} +57 -122
  18. llms/ui/SignIn.mjs +65 -0
  19. llms/ui/Welcome.mjs +8 -0
  20. llms/ui/ai.mjs +13 -117
  21. llms/ui/app.css +49 -1776
  22. llms/ui/index.mjs +171 -87
  23. llms/ui/lib/charts.mjs +13 -9
  24. llms/ui/lib/servicestack-vue.mjs +3 -3
  25. llms/ui/lib/vue.min.mjs +9 -10
  26. llms/ui/lib/vue.mjs +1602 -1763
  27. llms/ui/markdown.mjs +2 -10
  28. llms/ui/model-selector.mjs +686 -0
  29. llms/ui/tailwind.input.css +1 -55
  30. llms/ui/threadStore.mjs +583 -0
  31. llms/ui/utils.mjs +118 -113
  32. llms/ui.json +1069 -0
  33. {llms_py-3.0.0.dist-info → llms_py-3.0.0b2.dist-info}/METADATA +1 -1
  34. llms_py-3.0.0b2.dist-info/RECORD +58 -0
  35. llms/extensions/app/README.md +0 -20
  36. llms/extensions/app/__init__.py +0 -530
  37. llms/extensions/app/__pycache__/__init__.cpython-314.pyc +0 -0
  38. llms/extensions/app/__pycache__/db.cpython-314.pyc +0 -0
  39. llms/extensions/app/__pycache__/db_manager.cpython-314.pyc +0 -0
  40. llms/extensions/app/db.py +0 -644
  41. llms/extensions/app/db_manager.py +0 -195
  42. llms/extensions/app/requests.json +0 -9073
  43. llms/extensions/app/threads.json +0 -15290
  44. llms/extensions/app/ui/threadStore.mjs +0 -411
  45. llms/extensions/core_tools/CALCULATOR.md +0 -32
  46. llms/extensions/core_tools/__init__.py +0 -598
  47. llms/extensions/core_tools/__pycache__/__init__.cpython-314.pyc +0 -0
  48. llms/extensions/core_tools/ui/codemirror/addon/edit/closebrackets.js +0 -201
  49. llms/extensions/core_tools/ui/codemirror/addon/edit/closetag.js +0 -185
  50. llms/extensions/core_tools/ui/codemirror/addon/edit/continuelist.js +0 -101
  51. llms/extensions/core_tools/ui/codemirror/addon/edit/matchbrackets.js +0 -160
  52. llms/extensions/core_tools/ui/codemirror/addon/edit/matchtags.js +0 -66
  53. llms/extensions/core_tools/ui/codemirror/addon/edit/trailingspace.js +0 -27
  54. llms/extensions/core_tools/ui/codemirror/addon/selection/active-line.js +0 -72
  55. llms/extensions/core_tools/ui/codemirror/addon/selection/mark-selection.js +0 -119
  56. llms/extensions/core_tools/ui/codemirror/addon/selection/selection-pointer.js +0 -98
  57. llms/extensions/core_tools/ui/codemirror/doc/docs.css +0 -225
  58. llms/extensions/core_tools/ui/codemirror/doc/source_sans.woff +0 -0
  59. llms/extensions/core_tools/ui/codemirror/lib/codemirror.css +0 -344
  60. llms/extensions/core_tools/ui/codemirror/lib/codemirror.js +0 -9884
  61. llms/extensions/core_tools/ui/codemirror/mode/clike/clike.js +0 -942
  62. llms/extensions/core_tools/ui/codemirror/mode/javascript/index.html +0 -118
  63. llms/extensions/core_tools/ui/codemirror/mode/javascript/javascript.js +0 -962
  64. llms/extensions/core_tools/ui/codemirror/mode/javascript/typescript.html +0 -62
  65. llms/extensions/core_tools/ui/codemirror/mode/python/python.js +0 -402
  66. llms/extensions/core_tools/ui/codemirror/theme/dracula.css +0 -40
  67. llms/extensions/core_tools/ui/codemirror/theme/mocha.css +0 -135
  68. llms/extensions/core_tools/ui/index.mjs +0 -650
  69. llms/extensions/gallery/README.md +0 -61
  70. llms/extensions/gallery/__init__.py +0 -61
  71. llms/extensions/gallery/__pycache__/__init__.cpython-314.pyc +0 -0
  72. llms/extensions/gallery/__pycache__/db.cpython-314.pyc +0 -0
  73. llms/extensions/gallery/db.py +0 -298
  74. llms/extensions/gallery/ui/index.mjs +0 -482
  75. llms/extensions/katex/README.md +0 -39
  76. llms/extensions/katex/__init__.py +0 -6
  77. llms/extensions/katex/__pycache__/__init__.cpython-314.pyc +0 -0
  78. llms/extensions/katex/ui/README.md +0 -125
  79. llms/extensions/katex/ui/contrib/auto-render.js +0 -338
  80. llms/extensions/katex/ui/contrib/auto-render.min.js +0 -1
  81. llms/extensions/katex/ui/contrib/auto-render.mjs +0 -244
  82. llms/extensions/katex/ui/contrib/copy-tex.js +0 -127
  83. llms/extensions/katex/ui/contrib/copy-tex.min.js +0 -1
  84. llms/extensions/katex/ui/contrib/copy-tex.mjs +0 -105
  85. llms/extensions/katex/ui/contrib/mathtex-script-type.js +0 -109
  86. llms/extensions/katex/ui/contrib/mathtex-script-type.min.js +0 -1
  87. llms/extensions/katex/ui/contrib/mathtex-script-type.mjs +0 -24
  88. llms/extensions/katex/ui/contrib/mhchem.js +0 -3213
  89. llms/extensions/katex/ui/contrib/mhchem.min.js +0 -1
  90. llms/extensions/katex/ui/contrib/mhchem.mjs +0 -3109
  91. llms/extensions/katex/ui/contrib/render-a11y-string.js +0 -887
  92. llms/extensions/katex/ui/contrib/render-a11y-string.min.js +0 -1
  93. llms/extensions/katex/ui/contrib/render-a11y-string.mjs +0 -800
  94. llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.ttf +0 -0
  95. llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff +0 -0
  96. llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  97. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
  98. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
  99. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  100. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
  101. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
  102. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  103. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
  104. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff +0 -0
  105. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  106. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
  107. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff +0 -0
  108. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  109. llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.ttf +0 -0
  110. llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff +0 -0
  111. llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff2 +0 -0
  112. llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
  113. llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff +0 -0
  114. llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  115. llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.ttf +0 -0
  116. llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff +0 -0
  117. llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff2 +0 -0
  118. llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.ttf +0 -0
  119. llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff +0 -0
  120. llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff2 +0 -0
  121. llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
  122. llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff +0 -0
  123. llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  124. llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.ttf +0 -0
  125. llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff +0 -0
  126. llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff2 +0 -0
  127. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
  128. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff +0 -0
  129. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  130. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
  131. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff +0 -0
  132. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  133. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
  134. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff +0 -0
  135. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  136. llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.ttf +0 -0
  137. llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff +0 -0
  138. llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff2 +0 -0
  139. llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.ttf +0 -0
  140. llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff +0 -0
  141. llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  142. llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.ttf +0 -0
  143. llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff +0 -0
  144. llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  145. llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.ttf +0 -0
  146. llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff +0 -0
  147. llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  148. llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.ttf +0 -0
  149. llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff +0 -0
  150. llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  151. llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
  152. llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff +0 -0
  153. llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  154. llms/extensions/katex/ui/index.mjs +0 -92
  155. llms/extensions/katex/ui/katex-swap.css +0 -1230
  156. llms/extensions/katex/ui/katex-swap.min.css +0 -1
  157. llms/extensions/katex/ui/katex.css +0 -1230
  158. llms/extensions/katex/ui/katex.js +0 -19080
  159. llms/extensions/katex/ui/katex.min.css +0 -1
  160. llms/extensions/katex/ui/katex.min.js +0 -1
  161. llms/extensions/katex/ui/katex.min.mjs +0 -1
  162. llms/extensions/katex/ui/katex.mjs +0 -18547
  163. llms/extensions/providers/__init__.py +0 -18
  164. llms/extensions/providers/__pycache__/__init__.cpython-314.pyc +0 -0
  165. llms/extensions/providers/__pycache__/anthropic.cpython-314.pyc +0 -0
  166. llms/extensions/providers/__pycache__/chutes.cpython-314.pyc +0 -0
  167. llms/extensions/providers/__pycache__/google.cpython-314.pyc +0 -0
  168. llms/extensions/providers/__pycache__/nvidia.cpython-314.pyc +0 -0
  169. llms/extensions/providers/__pycache__/openai.cpython-314.pyc +0 -0
  170. llms/extensions/providers/__pycache__/openrouter.cpython-314.pyc +0 -0
  171. llms/extensions/providers/anthropic.py +0 -229
  172. llms/extensions/providers/chutes.py +0 -155
  173. llms/extensions/providers/google.py +0 -378
  174. llms/extensions/providers/nvidia.py +0 -105
  175. llms/extensions/providers/openai.py +0 -156
  176. llms/extensions/providers/openrouter.py +0 -72
  177. llms/extensions/system_prompts/README.md +0 -22
  178. llms/extensions/system_prompts/__init__.py +0 -45
  179. llms/extensions/system_prompts/__pycache__/__init__.cpython-314.pyc +0 -0
  180. llms/extensions/system_prompts/ui/index.mjs +0 -280
  181. llms/extensions/system_prompts/ui/prompts.json +0 -1067
  182. llms/extensions/tools/__init__.py +0 -5
  183. llms/extensions/tools/__pycache__/__init__.cpython-314.pyc +0 -0
  184. llms/extensions/tools/ui/index.mjs +0 -204
  185. llms/providers-extra.json +0 -356
  186. llms/ui/ctx.mjs +0 -365
  187. llms/ui/modules/chat/ChatBody.mjs +0 -691
  188. llms/ui/modules/chat/index.mjs +0 -828
  189. llms/ui/modules/layout.mjs +0 -243
  190. llms/ui/modules/model-selector.mjs +0 -851
  191. llms_py-3.0.0.dist-info/RECORD +0 -202
  192. {llms_py-3.0.0.dist-info → llms_py-3.0.0b2.dist-info}/WHEEL +0 -0
  193. {llms_py-3.0.0.dist-info → llms_py-3.0.0b2.dist-info}/entry_points.txt +0 -0
  194. {llms_py-3.0.0.dist-info → llms_py-3.0.0b2.dist-info}/licenses/LICENSE +0 -0
  195. {llms_py-3.0.0.dist-info → llms_py-3.0.0b2.dist-info}/top_level.txt +0 -0
llms/ui/utils.mjs CHANGED
@@ -1,5 +1,37 @@
1
- import { toRaw } from "vue"
2
- import { rightPart } from "@servicestack/client"
1
+ import { $$, createElement, rightPart, pick } from "@servicestack/client"
2
+
3
+ const cacheUrlInfo = {}
4
+
5
+ export function getCacheInfo(url) {
6
+ return cacheUrlInfo[url]
7
+ }
8
+ export async function fetchCacheInfos(urls) {
9
+ const infos = {}
10
+ const fetchInfos = []
11
+ for (const url of urls) {
12
+ const info = getCacheInfo(url)
13
+ if (info) {
14
+ infos[url] = info
15
+ } else {
16
+ fetchInfos.push(fetch(url + "?info"))
17
+ }
18
+ }
19
+ const responses = await Promise.all(fetchInfos)
20
+ for (let i = 0; i < urls.length; i++) {
21
+ try {
22
+ const info = await responses[i].json()
23
+ setCacheInfo(urls[i], info)
24
+ infos[urls[i]] = info
25
+ } catch (e) {
26
+ console.error('Failed to fetch info for', urls[i], e)
27
+ }
28
+ }
29
+ return infos
30
+ }
31
+
32
+ export function setCacheInfo(url, info) {
33
+ cacheUrlInfo[url] = info
34
+ }
3
35
 
4
36
  export function toJsonArray(json) {
5
37
  try {
@@ -51,6 +83,19 @@ export function fileToDataUri(file) {
51
83
  })
52
84
  }
53
85
 
86
+ export async function uploadFile(file) {
87
+ const formData = new FormData()
88
+ formData.append('file', file)
89
+ const response = await fetch('/upload', {
90
+ method: 'POST',
91
+ body: formData
92
+ })
93
+ if (!response.ok) {
94
+ throw new Error(`Upload failed: ${response.statusText}`)
95
+ }
96
+ return response.json()
97
+ }
98
+
54
99
  export function serializedClone(obj) {
55
100
  try {
56
101
  return JSON.parse(JSON.stringify(obj))
@@ -73,8 +118,7 @@ export function deepClone(o) {
73
118
  return structuredClone(o)
74
119
  } catch (e) {
75
120
  console.warn('structuredClone failed, falling back to JSON:', e)
76
- console.log(o)
77
- // console.log(JSON.stringify(o, undefined, 2))
121
+ console.log(JSON.stringify(o, undefined, 2))
78
122
  }
79
123
  }
80
124
 
@@ -84,114 +128,92 @@ export function deepClone(o) {
84
128
 
85
129
  export function toModelInfo(model) {
86
130
  if (!model) return undefined
87
- const props = ['id', 'name', 'provider', 'cost', 'modalities']
88
- const to = {}
89
- props.forEach(k => to[k] = toRaw(model[k]))
90
- return deepClone(to)
131
+ const { id, name, provider, cost, modalities } = model
132
+ return deepClone({ id, name, provider, cost, modalities })
91
133
  }
92
134
 
93
- export function pluralize(word, count) {
94
- return count === 1 ? word : word + 's'
95
- }
96
-
97
- const currFmt2 = new Intl.NumberFormat(undefined, { style: 'currency', currency: 'USD', maximumFractionDigits: 2 })
98
- const currFmt6 = new Intl.NumberFormat(undefined, { style: 'currency', currency: 'USD', maximumFractionDigits: 6 })
99
-
100
- export function tokenCost(price, tokens = 1) {
135
+ const numFmt = new Intl.NumberFormat(undefined, { style: 'currency', currency: 'USD', maximumFractionDigits: 6 })
136
+ export function tokenCost(price) {
101
137
  if (!price) return ''
102
- var ret = currFmt2.format(parseFloat(price) * tokens)
138
+ var ret = numFmt.format(parseFloat(price))
103
139
  return ret.endsWith('.00') ? ret.slice(0, -3) : ret
104
140
  }
105
- export function tokenCostLong(price, tokens = 1) {
106
- if (!price) return ''
107
- const ret = currFmt6.format(parseFloat(price) * tokens)
108
- return ret.endsWith('.000000') ? ret.slice(0, -7) : ret
109
- }
110
141
  export function formatCost(cost) {
111
142
  if (!cost) return ''
112
- return currFmt2.format(parseFloat(cost))
143
+ return numFmt.format(parseFloat(cost))
113
144
  }
114
- export function tokensTitle(usage) {
145
+ export function statsTitle(stats) {
115
146
  let title = []
116
- if (usage.tokens && usage.price) {
117
- const msg = parseFloat(usage.price) > 0
118
- ? `${usage.tokens} tokens @ ${usage.price} = ${tokenCostLong(usage.price, usage.tokens)}`
119
- : `${usage.tokens} tokens`
120
- const duration = usage.duration ? ` in ${usage.duration}ms` : ''
121
- title.push(msg + duration)
147
+ // Each stat on its own line
148
+ if (stats.cost) {
149
+ title.push(`Total Cost: ${formatCost(stats.cost)}`)
122
150
  }
123
- return title.join('\n')
124
- }
125
-
126
- // Accessible in views via $fmt
127
- export function utilsFormatters() {
128
- function relativeTime(timestamp) {
129
- const now = new Date()
130
- const date = new Date(timestamp)
131
- const diffInSeconds = Math.floor((now - date) / 1000)
132
-
133
- if (diffInSeconds < 60) return 'Just now'
134
- if (diffInSeconds < 3600) return `${Math.floor(diffInSeconds / 60)}m ago`
135
- if (diffInSeconds < 86400) return `${Math.floor(diffInSeconds / 3600)}h ago`
136
- if (diffInSeconds < 604800) return `${Math.floor(diffInSeconds / 86400)}d ago`
137
-
138
- return date.toLocaleDateString()
151
+ if (stats.inputTokens) {
152
+ title.push(`Input Tokens: ${stats.inputTokens}`)
139
153
  }
140
- function costLong(cost) {
141
- if (!cost) return ''
142
- const ret = currFmt6.format(parseFloat(cost))
143
- return ret.endsWith('.000000') ? ret.slice(0, -7) : ret
154
+ if (stats.outputTokens) {
155
+ title.push(`Output Tokens: ${stats.outputTokens}`)
144
156
  }
145
- function statsTitle(stats) {
146
- let title = []
147
- // Each stat on its own line
148
- if (stats.cost) {
149
- title.push(`Total Cost: ${costLong(stats.cost)}`)
150
- }
151
- if (stats.inputTokens) {
152
- title.push(`Input Tokens: ${stats.inputTokens}`)
153
- }
154
- if (stats.outputTokens) {
155
- title.push(`Output Tokens: ${stats.outputTokens}`)
156
- }
157
- if (stats.requests) {
158
- title.push(`Requests: ${stats.requests}`)
159
- }
160
- if (stats.duration) {
161
- title.push(`Duration: ${stats.duration}ms`)
162
- }
163
- return title.join('\n')
157
+ if (stats.requests) {
158
+ title.push(`Requests: ${stats.requests}`)
164
159
  }
165
-
166
- function time(timestamp) {
167
- return new Date(timestamp).toLocaleTimeString([], {
168
- hour: '2-digit',
169
- minute: '2-digit'
170
- })
160
+ if (stats.duration) {
161
+ title.push(`Duration: ${stats.duration}ms`)
171
162
  }
163
+ return title.join('\n')
164
+ }
172
165
 
173
- function shortDate(ts) {
174
- if (!ts) return ''
175
- const date = typeof ts === 'number' ? new Date(ts * 1000) : new Date(ts)
176
- return date.toLocaleDateString(undefined, {
177
- month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit'
166
+ const svg = {
167
+ clipboard: `<svg class="w-6 h-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="none"><path d="M8 5H6a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-1M8 5a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2M8 5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2m0 0h2a2 2 0 0 1 2 2v3m2 4H10m0 0l3-3m-3 3l3 3" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></g></svg>`,
168
+ check: `<svg class="w-6 h-6 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path></svg>`,
169
+ }
170
+
171
+ function copyBlock(btn) {
172
+ // console.log('copyBlock',btn)
173
+ const label = btn.previousElementSibling
174
+ const code = btn.parentElement.nextElementSibling
175
+ label.classList.remove('hidden')
176
+ label.innerHTML = 'copied'
177
+ btn.classList.add('border-gray-600', 'bg-gray-700')
178
+ btn.classList.remove('border-gray-700')
179
+ btn.innerHTML = svg.check
180
+ navigator.clipboard.writeText(code.innerText)
181
+ setTimeout(() => {
182
+ label.classList.add('hidden')
183
+ label.innerHTML = ''
184
+ btn.innerHTML = svg.clipboard
185
+ btn.classList.remove('border-gray-600', 'bg-gray-700')
186
+ btn.classList.add('border-gray-700')
187
+ }, 2000)
188
+ }
189
+
190
+ export function addCopyButtonToCodeBlocks(sel) {
191
+ globalThis.copyBlock ??= copyBlock
192
+ //console.log('addCopyButtonToCodeBlocks', sel, [...$$(sel)].length)
193
+
194
+ $$(sel).forEach(code => {
195
+ let pre = code.parentElement;
196
+ if (pre.classList.contains('group')) return
197
+ pre.classList.add('relative', 'group')
198
+
199
+ const div = createElement('div', { attrs: { className: 'opacity-0 group-hover:opacity-100 transition-opacity duration-100 flex absolute right-2 -mt-1 select-none' } })
200
+ const label = createElement('div', { attrs: { className: 'hidden font-sans p-1 px-2 mr-1 rounded-md border border-gray-600 bg-gray-700 text-gray-400' } })
201
+ const btn = createElement('button', {
202
+ attrs: {
203
+ type: 'button',
204
+ className: 'p-1 rounded-md border block text-gray-500 hover:text-gray-400 border-gray-700 hover:border-gray-600',
205
+ onclick: 'copyBlock(this)'
206
+ }
178
207
  })
179
- }
180
-
208
+ btn.innerHTML = svg.clipboard
209
+ div.appendChild(label)
210
+ div.appendChild(btn)
211
+ pre.insertBefore(div, code)
212
+ })
213
+ }
181
214
 
182
- return {
183
- currFmt: currFmt2,
184
- tokenCost,
185
- tokenCostLong,
186
- tokensTitle,
187
- cost: formatCost,
188
- costLong,
189
- statsTitle,
190
- relativeTime,
191
- time,
192
- pluralize,
193
- shortDate,
194
- }
215
+ export function addCopyButtons() {
216
+ addCopyButtonToCodeBlocks('.prose pre>code')
195
217
  }
196
218
 
197
219
  /**
@@ -204,21 +226,4 @@ export const nextId = (() => {
204
226
  last = (now > last) ? now : last + 1
205
227
  return last
206
228
  }
207
- })();
208
-
209
- export function utilsFunctions() {
210
- return {
211
- nextId,
212
- toJsonArray,
213
- toJsonObject,
214
- storageArray,
215
- storageObject,
216
- fileToBase64,
217
- fileToDataUri,
218
- serializedClone,
219
- deepClone,
220
- toModelInfo,
221
- pluralize,
222
- }
223
- }
224
-
229
+ })();