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
@@ -1,650 +0,0 @@
1
- import { ref, onMounted, watch, inject, nextTick } from "vue"
2
- import { ApiResult, createErrorStatus } from "@servicestack/client"
3
-
4
- let ext
5
-
6
- const languages = {
7
- python: {
8
- name: 'Python',
9
- mime: 'text/x-python',
10
- default: 'print("Hello, Python!")\n',
11
- },
12
- javascript: {
13
- name: 'JavaScript',
14
- mime: 'text/javascript',
15
- default: 'console.log("Hello, JavaScript!");\n',
16
- },
17
- typescript: {
18
- name: 'TypeScript',
19
- mime: 'text/typescript',
20
- default: 'const msg: string = "Hello, TypeScript!";\nconsole.log(msg);\n',
21
- },
22
- csharp: {
23
- name: 'C#',
24
- mime: 'text/x-csharp',
25
- default: 'Console.WriteLine("Hello, C#!");\n',
26
- },
27
- }
28
-
29
- const CodePage = {
30
- template: `
31
- <div class="flex flex-col h-full w-full">
32
- <component :is="'style'">
33
- .CodeMirror { height: 100% !important; }
34
- </component>
35
- <!-- Toolbar -->
36
- <div class="flex items-center justify-between p-2 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800 shrink-0">
37
- <div class="flex items-center space-x-1">
38
- <button v-for="lang in Object.keys(languages)" :key="lang" type="button" @click="language = lang"
39
- class="px-2.5 py-1 rounded-full text-xs font-medium border transition-colors select-none capitalize"
40
- :class="language === lang
41
- ? 'bg-blue-100 dark:bg-blue-900/40 text-blue-800 dark:text-blue-300 border-blue-200 dark:border-blue-800'
42
- : 'bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700'">
43
- {{ languages[lang].name }}
44
- </button>
45
- </div>
46
- <div class="flex items-center space-x-2">
47
- <button @click="toggleOutput" class="p-1 rounded hover:bg-gray-200 dark:hover:bg-gray-700 text-gray-500" :title="showOutput ? 'Hide Output' : 'Show Output'">
48
- <svg v-if="showOutput" xmlns="http://www.w3.org/2000/svg" class="size-5" viewBox="0 0 24 24"><path fill="currentColor" d="M21 3H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2m0 16H3v-3h18zm0-5H3V5h18z"/></svg>
49
- <svg v-else xmlns="http://www.w3.org/2000/svg" class="size-5" viewBox="0 0 24 24"><path fill="currentColor" d="M21 3H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2m0 16H3V5h18z"/></svg>
50
- </button>
51
- <button @click="runCode" type="button" :disabled="loading" class="px-4 py-1.5 bg-blue-600 text-white rounded hover:bg-blue-700 focus:outline-none disabled:opacity-50 disabled:cursor-not-allowed flex items-center shadow-sm transition-colors">
52
- <svg v-if="loading" class="animate-spin -ml-1 mr-2 h-4 w-4 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
53
- <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
54
- <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
55
- </svg>
56
- <span v-else>Run</span>
57
- <svg v-if="!loading" class="ml-1 size-5" fill="none" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="currentColor" d="M19.266 13.516a1.917 1.917 0 0 0 0-3.032A35.8 35.8 0 0 0 9.35 5.068l-.653-.232c-1.248-.443-2.567.401-2.736 1.69a42.5 42.5 0 0 0 0 10.948c.17 1.289 1.488 2.133 2.736 1.69l.653-.232a35.8 35.8 0 0 0 9.916-5.416"/></svg>
58
- </button>
59
- </div>
60
- </div>
61
-
62
- <!-- Main Content -->
63
- <div class="flex-1 flex flex-col min-h-0">
64
- <!-- Code Editor -->
65
- <div class="flex-1 overflow-hidden relative">
66
- <!-- The div CodeMirror attaches to. We use absolute positioning to ensure it takes full space of parent -->
67
- <div ref="refInput" class="absolute inset-0 h-full w-full text-base"></div>
68
- </div>
69
-
70
- <!-- Output Pane -->
71
- <div v-if="showOutput" class="h-1/3 min-h-[150px] border-t border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-900 flex flex-col font-mono text-sm overflow-hidden shrink-0 shadow-[0_-4px_6px_-1px_rgba(0,0,0,0.1)] z-10">
72
- <div class="px-2 py-1 bg-gray-100 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 text-xs font-semibold text-gray-500 uppercase flex justify-between items-center select-none">
73
- <span>Output</span>
74
- <div class="flex items-center">
75
- <span v-if="resultStatus" class="mr-2 px-2 py-0.5 rounded text-[10px]" :class="resultStatusColor">{{ resultStatus }}</span>
76
- <button @click="showOutput=false" type="button" class="hover:text-gray-700 dark:hover:text-gray-300">
77
- <svg xmlns="http://www.w3.org/2000/svg" class="size-4" viewBox="0 0 24 24"><path fill="currentColor" d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
78
- </button>
79
- </div>
80
- </div>
81
- <div class="flex-1 overflow-auto p-2 whitespace-pre-wrap font-mono relative">
82
- <div v-if="loading" class="absolute inset-0 bg-white/50 dark:bg-gray-900/50 flex items-center justify-center z-10 transition-opacity">
83
- <div class="animate-pulse text-blue-500">Executing...</div>
84
- </div>
85
- <div v-if="!stdout && !stderr && !resultStatus && !loading" class="text-gray-400 italic p-4 text-center">
86
- Press CTRL+ENTER or click Run to execute code.
87
- </div>
88
- <div v-if="stdout" class="text-gray-800 dark:text-gray-300">{{ stdout }}</div>
89
- <div v-if="stderr" class="text-red-600 dark:text-red-400 mt-2 border-t border-red-200 dark:border-red-900 pt-2">{{ stderr }}</div>
90
- </div>
91
- </div>
92
- </div>
93
- </div>
94
- `,
95
- setup() {
96
- let cm
97
- const refInput = ref()
98
- const language = ref(localStorage.getItem('llms.tools.lastLanguage') || 'python')
99
- const code = ref(localStorage.getItem(`llms.tools.${language.value}`) || '')
100
- const stdout = ref('')
101
- const stderr = ref('')
102
- const loading = ref(false)
103
- const resultStatus = ref('')
104
- const resultStatusColor = ref('')
105
- const showOutput = ref(true)
106
-
107
- const loadCode = (lang) => {
108
- const saved = localStorage.getItem(`llms.tools.${lang}`)
109
- // Default snippets if empty
110
- if (!saved || Object.values(languages).some(l => l.default.trim() === saved.trim())) {
111
- return languages[lang].default
112
- }
113
- return saved
114
- }
115
-
116
- // Initial load
117
- code.value = loadCode(language.value)
118
-
119
-
120
-
121
- watch(language, (newLang, oldLang) => {
122
- // Save old language code
123
- if (oldLang && cm) {
124
- const currentContent = cm.getValue()
125
- localStorage.setItem(`llms.tools.${oldLang}`, currentContent)
126
- }
127
- localStorage.setItem('llms.tools.lastLanguage', newLang)
128
-
129
- // Load new language code
130
- code.value = loadCode(newLang)
131
- if (cm) {
132
- cm.setValue(code.value)
133
- cm.setOption('mode', languages[newLang].mime)
134
- }
135
-
136
- // Clear output on language switch
137
- stdout.value = ''
138
- stderr.value = ''
139
- resultStatus.value = ''
140
- })
141
-
142
- function setError(status) {
143
- if (!status) return
144
- if (typeof status == 'string') {
145
- status = {
146
- message: status,
147
- errorCode: 'Error'
148
- }
149
- }
150
- stderr.value = status.message
151
- resultStatus.value = status.errorCode || 'Error'
152
- resultStatusColor.value = 'text-red-600 bg-red-100 dark:text-red-400 dark:bg-red-900'
153
- }
154
-
155
- const toggleOutput = () => {
156
- showOutput.value = !showOutput.value
157
- nextTick(() => {
158
- if (cm) cm.refresh()
159
- })
160
- }
161
-
162
- const runCode = async () => {
163
- if (loading.value) return
164
-
165
- if (!showOutput.value) {
166
- showOutput.value = true
167
- nextTick(() => {
168
- if (cm) cm.refresh()
169
- })
170
- }
171
-
172
- // Save before run
173
- if (cm) {
174
- code.value = cm.getValue()
175
- }
176
- localStorage.setItem(`llms.tools.${language.value}`, code.value)
177
-
178
- loading.value = true
179
- stdout.value = ''
180
- stderr.value = ''
181
- resultStatus.value = ''
182
- let api
183
-
184
- try {
185
- const res = await ext.post(`/code/${language.value}/run`, {
186
- body: code.value
187
- })
188
- if (!res.ok) {
189
- api = new ApiResult({ error: createErrorStatus(`HTTP ${res.status} ${res.statusText}`) })
190
- } else {
191
- const response = await res.json()
192
- api = new ApiResult({ response })
193
- }
194
- } catch (e) {
195
- api = new ApiResult({ error: createErrorStatus(e.message) })
196
- }
197
-
198
- if (api.response) {
199
- const result = api.response
200
- stdout.value = result.stdout || ''
201
- stderr.value = result.stderr || ''
202
-
203
- if (result.returncode === 0) {
204
- resultStatus.value = 'Success'
205
- resultStatusColor.value = 'text-green-600 bg-green-100 dark:text-green-400 dark:bg-green-900'
206
- } else {
207
- resultStatus.value = `Exit Code: ${result.returncode}`
208
- resultStatusColor.value = 'text-red-600 bg-red-100 dark:text-red-400 dark:bg-red-900'
209
- }
210
- }
211
- else if (api.error) {
212
- setError(api.error)
213
- }
214
-
215
- loading.value = false
216
- }
217
-
218
- onMounted(() => {
219
- // Ensure CodeMirror is global
220
- if (typeof CodeMirror === 'undefined') {
221
- console.error('CodeMirror is not loaded')
222
- return
223
- }
224
-
225
- cm = CodeMirror(refInput.value, {
226
- lineNumbers: true,
227
- styleActiveLine: true,
228
- matchBrackets: true,
229
- mode: languages[language.value].mime,
230
- theme: 'ctp-mocha', // using the theme from existing code
231
- value: code.value,
232
- extraKeys: {
233
- "Ctrl-Enter": () => runCode(),
234
- "Cmd-Enter": () => runCode(), // Mac support
235
- },
236
- tabSize: 4,
237
- indentUnit: 4,
238
- lineWrapping: false, // Code editors usually don't wrap by default, but customizable
239
- })
240
-
241
- cm.on('change', () => {
242
- code.value = cm.getValue()
243
- localStorage.setItem(`llms.tools.${language.value}`, code.value)
244
- })
245
-
246
- // Fix layout issues when resizing
247
- window.addEventListener('resize', () => {
248
- cm.refresh()
249
- })
250
- })
251
-
252
- return {
253
- languages,
254
- refInput,
255
- stdout,
256
- stderr,
257
- loading,
258
- resultStatus,
259
- resultStatusColor,
260
- language,
261
- code,
262
- showOutput,
263
- toggleOutput,
264
- runCode,
265
- }
266
- }
267
- }
268
-
269
- const CalcPage = {
270
- template: `
271
- <div class="flex flex-col h-full w-full bg-white dark:bg-gray-900 text-base">
272
- <!-- Header/Input Area -->
273
- <div class="p-4 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800 shrink-0">
274
- <div class="max-w-3xl mx-auto w-full">
275
- <form @submit.prevent="calculate" class="relative">
276
- <input
277
- ref="inputRef"
278
- v-model="expression"
279
- type="text"
280
- placeholder="Type an expression (e.g. 1 + 2 * 3) and press Enter"
281
- class="w-full px-4 py-3 pr-12 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none shadow-sm transition-all"
282
- :disabled="loading"
283
- autofocus
284
- />
285
- <button
286
- type="submit"
287
- :disabled="loading || !expression.trim()"
288
- class="absolute right-2 top-1/2 -translate-y-1/2 p-2 text-blue-600 dark:text-blue-400 hover:bg-blue-50 dark:hover:bg-gray-600 rounded-md transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
289
- title="Calculate"
290
- >
291
- <svg v-if="loading" class="animate-spin size-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
292
- <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
293
- <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
294
- </svg>
295
- <svg v-else class="size-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" d="M16 14H8m8-4H8"/><circle cx="12" cy="12" r="10"/></g></svg>
296
- </button>
297
- </form>
298
- <div v-if="error" class="mt-2 text-sm text-red-600 dark:text-red-400">
299
- {{ error }}
300
- </div>
301
- </div>
302
- </div>
303
-
304
- <!-- History List -->
305
- <div class="flex-1 overflow-auto p-4">
306
- <div class="max-w-3xl mx-auto w-full space-y-3">
307
- <div v-if="history.length === 0" class="text-center text-gray-400 dark:text-gray-500 py-10 italic">
308
- No calculation history.
309
- </div>
310
-
311
- <div v-for="(item, index) in history" :key="index" class="group bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg p-4 shadow-sm hover:shadow-md transition-all">
312
- <div class="flex items-center justify-between gap-4">
313
- <div class="flex-1 space-y-1 min-w-0">
314
- <!-- Expression -->
315
- <div class="flex items-center gap-2 group/expr cursor-pointer" @click="useResult(item.expression, item, 'expr')">
316
- <span
317
- class="font-mono text-gray-500 dark:text-gray-400 group-hover/expr:text-blue-600 dark:group-hover/expr:text-blue-400 transition-colors select-none"
318
- title="Click to copy & use"
319
- >
320
- {{ item.expression }} =
321
- </span>
322
- <button
323
- type="button"
324
- class="opacity-0 group-hover/expr:opacity-100 p-1 text-gray-400 group-hover/expr:text-blue-500 transition-opacity"
325
- title="Copy expression"
326
- >
327
- <svg v-if="item.copiedExpr" class="size-3.5 text-green-600 dark:text-green-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="m9.55 18l-5.7-5.7l1.425-1.425L9.55 15.15l9.175-9.175L20.15 7.4z"/></svg>
328
- <svg v-else xmlns="http://www.w3.org/2000/svg" class="size-3.5" viewBox="0 0 24 24"><path fill="currentColor" d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2m0 16H8V7h11z"/></svg>
329
- </button>
330
- </div>
331
-
332
- <!-- Answer -->
333
- <div class="flex items-center gap-2 group/ans cursor-pointer" @click="useResult(item.answer, item, 'ans')">
334
- <span
335
- class="font-mono text-xl font-semibold text-gray-900 dark:text-white group-hover/ans:text-blue-600 dark:group-hover/ans:text-blue-400 transition-colors break-all"
336
- title="Click to copy & use"
337
- >
338
- {{ item.answer }}
339
- </span>
340
- <button
341
- type="button"
342
- class="opacity-0 group-hover/ans:opacity-100 p-1 text-gray-400 group-hover/ans:text-blue-500 transition-opacity"
343
- title="Copy answer"
344
- >
345
- <svg v-if="item.copiedAns" class="size-4 text-green-600 dark:text-green-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="m9.55 18l-5.7-5.7l1.425-1.425L9.55 15.15l9.175-9.175L20.15 7.4z"/></svg>
346
- <svg v-else xmlns="http://www.w3.org/2000/svg" class="size-4" viewBox="0 0 24 24"><path fill="currentColor" d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2m0 16H8V7h11z"/></svg>
347
- </button>
348
- </div>
349
- </div>
350
-
351
- <!-- Delete Button -->
352
- <button
353
- type="button"
354
- @click="remove(index)"
355
- class="opacity-0 group-hover:opacity-100 p-2 text-gray-400 hover:text-red-500 hover:bg-red-50 dark:hover:bg-red-900/30 rounded-md transition-all"
356
- title="Delete"
357
- >
358
- <svg class="size-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path></svg>
359
- </button>
360
- </div>
361
- </div>
362
-
363
- <div v-if="history.length" class="flex justify-center pt-4">
364
- <button
365
- type="button"
366
- @click="clearAll"
367
- class="text-sm text-gray-500 hover:text-red-600 dark:text-gray-400 dark:hover:text-red-300"
368
- >
369
- clear all history
370
- </button>
371
- </div>
372
-
373
- <!-- Features (Operators & Functions) -->
374
- <div v-if="features.operators?.length || features.functions?.length" class="mt-12 mb-6 space-y-4">
375
- <!-- Numbers -->
376
- <div>
377
- <div class="flex flex-wrap gap-2">
378
- <button
379
- v-for="num in features.numbers"
380
- :key="num"
381
- type="button"
382
- @click="insert(num)"
383
- class="px-3 py-1 bg-gray-100 dark:bg-gray-800 hover:bg-blue-100 dark:hover:bg-blue-900/30 text-gray-700 dark:text-gray-300 hover:text-blue-700 dark:hover:text-blue-300 border border-gray-200 dark:border-gray-700 rounded text-sm font-mono transition-colors"
384
- :title="'insert number ' + num"
385
- >
386
- {{ num }}
387
- </button>
388
- <span class="px-1 py-1 text-gray-400 dark:text-gray-600">|</span>
389
- <button
390
- v-for="c in features.constants"
391
- :key="c"
392
- type="button"
393
- @click="insert(c)"
394
- class="px-3 py-1 bg-gray-100 dark:bg-gray-800 hover:bg-blue-100 dark:hover:bg-blue-900/30 text-gray-700 dark:text-gray-300 hover:text-blue-700 dark:hover:text-blue-300 border border-gray-200 dark:border-gray-700 rounded text-sm font-mono transition-colors"
395
- :title="'insert constant ' + c"
396
- >
397
- {{ c }}
398
- </button>
399
- </div>
400
- </div>
401
-
402
- <!-- Operators -->
403
- <div v-if="features.operators?.length">
404
- <div class="flex flex-wrap gap-2">
405
- <button
406
- v-for="op in features.operators"
407
- :key="op"
408
- type="button"
409
- @click="insert(op)"
410
- class="px-3 py-1 bg-gray-100 dark:bg-gray-800 hover:bg-blue-100 dark:hover:bg-blue-900/30 text-gray-700 dark:text-gray-300 hover:text-blue-700 dark:hover:text-blue-300 border border-gray-200 dark:border-gray-700 rounded text-sm font-mono transition-colors"
411
- :title="'insert operator ' + op"
412
- >
413
- {{ op }}
414
- </button>
415
- </div>
416
- </div>
417
-
418
- <!-- Functions -->
419
- <div v-if="features.functions?.length">
420
- <h3 class="text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2">Functions</h3>
421
- <div class="flex flex-wrap gap-2">
422
- <button
423
- v-for="func in features.functions"
424
- :key="func"
425
- type="button"
426
- @click="wrapWithFunction(func)"
427
- class="px-3 py-1 bg-gray-100 dark:bg-gray-800 hover:bg-purple-100 dark:hover:bg-purple-900/30 text-gray-700 dark:text-gray-300 hover:text-purple-700 dark:hover:text-purple-300 border border-gray-200 dark:border-gray-700 rounded text-sm font-mono transition-colors"
428
- :title="'use function ' + func"
429
- >
430
- {{ func }}
431
- </button>
432
- </div>
433
- </div>
434
- </div>
435
- </div>
436
- </div>
437
- </div>
438
- `,
439
- setup() {
440
- const ctx = inject('ctx')
441
- const expression = ref('')
442
- const history = ref([])
443
- const loading = ref(false)
444
- const error = ref('')
445
- const inputRef = ref()
446
- const features = ref({ functions: [] })
447
-
448
- // Load history from localStorage
449
- try {
450
- const saved = localStorage.getItem('llms.tools.calc.history')
451
- if (saved) {
452
- history.value = JSON.parse(saved)
453
- }
454
- } catch (e) {
455
- console.error('Failed to load history', e)
456
- }
457
-
458
- function setExpr(result) {
459
- if (Array.isArray(result)) {
460
- expression.value = JSON.stringify(result)
461
- } else {
462
- expression.value = String(result)
463
- }
464
- }
465
-
466
- const saveHistory = () => {
467
- localStorage.setItem('llms.tools.calc.history', JSON.stringify(history.value))
468
- }
469
-
470
- const calculate = async () => {
471
- if (!expression.value.trim() || loading.value) return
472
-
473
- loading.value = true
474
- error.value = ''
475
- const expr = expression.value
476
-
477
- const res = await ext.post('/calc', {
478
- body: expr
479
- })
480
-
481
- const api = await ext.createJsonResult(res)
482
-
483
- if (api.response) {
484
- // Add to history (newest first)
485
- history.value.unshift({
486
- expression: expr,
487
- answer: api.response.result,
488
- timestamp: Date.now()
489
- })
490
-
491
- // Keep history size reasonable
492
- if (history.value.length > 50) {
493
- history.value = history.value.slice(0, 50)
494
- }
495
-
496
- saveHistory()
497
- setExpr(api.response.result)
498
- } else {
499
- error.value = api.error.message
500
- }
501
-
502
- loading.value = false
503
- // Refocus input and move cursor to end
504
- nextTick(() => {
505
- if (inputRef.value) {
506
- inputRef.value.focus()
507
- const len = inputRef.value.value.length
508
- inputRef.value.setSelectionRange(len, len)
509
- }
510
- })
511
- }
512
-
513
- const populate = (result) => {
514
- setExpr(result)
515
- inputRef.value?.focus()
516
- }
517
-
518
- const insert = (text) => {
519
- expression.value += String(text)
520
- inputRef.value?.focus()
521
- }
522
-
523
- const wrapWithFunction = (fn) => {
524
- const input = inputRef.value
525
- if (!input) return
526
-
527
- const start = input.selectionStart
528
- const end = input.selectionEnd
529
- const val = expression.value
530
-
531
- if (start !== end) {
532
- // Wrap selection
533
- const selected = val.substring(start, end)
534
- const before = val.substring(0, start)
535
- const after = val.substring(end)
536
- expression.value = `${before}${fn}(${selected})${after}`
537
-
538
- nextTick(() => {
539
- input.focus()
540
- // Position cursor after the closing parenthesis
541
- const newPos = start + fn.length + 1 + selected.length + 1
542
- input.setSelectionRange(newPos, newPos)
543
- })
544
- } else if (val) {
545
- // Wrap entire expression
546
- expression.value = `${fn}(${val})`
547
- nextTick(() => {
548
- input.focus()
549
- // Position cursor at end
550
- const len = expression.value.length
551
- input.setSelectionRange(len, len)
552
- })
553
- } else {
554
- // Just insert empty function
555
- expression.value = `${fn}()`
556
- nextTick(() => {
557
- input.focus()
558
- // Position cursor inside parentheses
559
- const pos = fn.length + 1
560
- input.setSelectionRange(pos, pos)
561
- })
562
- }
563
- }
564
-
565
- const copy = (text) => {
566
- navigator.clipboard.writeText(String(text))
567
- }
568
-
569
- const useResult = (text, item, type) => {
570
- populate(text)
571
- const str = String(text)
572
- copy(str)
573
-
574
- // Set temporary success state
575
- if (type === 'expr') item.copiedExpr = true
576
- else if (type === 'ans') item.copiedAns = true
577
-
578
- setTimeout(() => {
579
- if (type === 'expr') item.copiedExpr = false
580
- else if (type === 'ans') item.copiedAns = false
581
- }, 2000)
582
-
583
- ctx.toast('Copied to clipboard')
584
- }
585
-
586
- const remove = (index) => {
587
- history.value.splice(index, 1)
588
- saveHistory()
589
- }
590
-
591
- const clearAll = () => {
592
- if (confirm('Are you sure you want to clear all history?')) {
593
- history.value = []
594
- saveHistory()
595
- }
596
- }
597
-
598
- onMounted(async () => {
599
- const api = await ext.getJson('/calc')
600
- features.value = api.response
601
- console.log(features.value)
602
- })
603
-
604
- return {
605
- expression,
606
- history,
607
- loading,
608
- error,
609
- inputRef,
610
- calculate,
611
- useResult,
612
- remove,
613
- clearAll,
614
- features,
615
- insert,
616
- wrapWithFunction,
617
- }
618
- }
619
- }
620
-
621
- export default {
622
- install(ctx) {
623
- ext = ctx.scope('core_tools')
624
-
625
- ctx.setLeftIcons({
626
- code: {
627
- component: {
628
- template: `<svg @click="$ctx.togglePath('/code')" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="none"><path d="M0 0h24v24H0z"/><path fill="currentColor" d="M14.486 3.143a1 1 0 0 1 .692 1.233l-4.43 15.788a1 1 0 0 1-1.926-.54l4.43-15.788a1 1 0 0 1 1.234-.693M7.207 7.05a1 1 0 0 1 0 1.414L3.672 12l3.535 3.535a1 1 0 1 1-1.414 1.415L1.55 12.707a1 1 0 0 1 0-1.414L5.793 7.05a1 1 0 0 1 1.414 0m9.586 1.414a1 1 0 1 1 1.414-1.414l4.243 4.243a1 1 0 0 1 0 1.414l-4.243 4.243a1 1 0 0 1-1.414-1.415L20.328 12z"/></g></svg>`
629
- },
630
- isActive({ path }) {
631
- return path === '/code'
632
- },
633
- title: 'Run Code',
634
- },
635
- calc: {
636
- component: {
637
- template: `<svg @click="$ctx.togglePath('/calc')" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 14"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"><path d="M11.5.5h-9a1 1 0 0 0-1 1v11a1 1 0 0 0 1 1h9a1 1 0 0 0 1-1v-11a1 1 0 0 0-1-1m-10 5h11"/><path d="M4.25 8.5a.25.25 0 0 1 0-.5m0 .5a.25.25 0 0 0 0-.5M7 8.5A.25.25 0 0 1 7 8m0 .5A.25.25 0 0 0 7 8m2.75.5a.25.25 0 0 1 0-.5m0 .5a.25.25 0 0 0 0-.5m-5.5 3a.25.25 0 1 1 0-.5m0 .5a.25.25 0 1 0 0-.5M7 11a.25.25 0 1 1 0-.5m0 .5a.25.25 0 1 0 0-.5m2.75.5a.25.25 0 1 1 0-.5m0 .5a.25.25 0 1 0 0-.5M10 3H9"/></g></svg>`,
638
- },
639
- isActive({ path }) {
640
- return path === '/calc'
641
- },
642
- title: 'Calculator',
643
- }
644
- })
645
-
646
- ctx.routes.push({ path: '/code', component: CodePage, meta: { title: 'Run Code' } })
647
- ctx.routes.push({ path: '/calc', component: CalcPage, meta: { title: 'Calculator' } })
648
-
649
- }
650
- }