llms-py 3.0.0__py3-none-any.whl → 3.0.0b1__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 (206) hide show
  1. llms/index.html +77 -35
  2. llms/llms.json +23 -72
  3. llms/main.py +732 -1786
  4. llms/providers.json +1 -1
  5. llms/{extensions/analytics/ui/index.mjs → ui/Analytics.mjs} +238 -154
  6. llms/ui/App.mjs +60 -151
  7. llms/ui/Avatar.mjs +85 -0
  8. llms/ui/Brand.mjs +52 -0
  9. llms/ui/ChatPrompt.mjs +606 -0
  10. llms/ui/Main.mjs +873 -0
  11. llms/ui/ModelSelector.mjs +693 -0
  12. llms/ui/OAuthSignIn.mjs +92 -0
  13. llms/ui/ProviderIcon.mjs +36 -0
  14. llms/ui/ProviderStatus.mjs +105 -0
  15. llms/{extensions/app/ui → ui}/Recents.mjs +65 -91
  16. llms/ui/{modules/chat/SettingsDialog.mjs → SettingsDialog.mjs} +9 -9
  17. llms/{extensions/app/ui/index.mjs → ui/Sidebar.mjs} +58 -124
  18. llms/ui/SignIn.mjs +64 -0
  19. llms/ui/SystemPromptEditor.mjs +31 -0
  20. llms/ui/SystemPromptSelector.mjs +56 -0
  21. llms/ui/Welcome.mjs +8 -0
  22. llms/ui/ai.mjs +53 -125
  23. llms/ui/app.css +111 -1837
  24. llms/ui/lib/charts.mjs +13 -9
  25. llms/ui/lib/servicestack-vue.mjs +3 -3
  26. llms/ui/lib/vue.min.mjs +9 -10
  27. llms/ui/lib/vue.mjs +1602 -1763
  28. llms/ui/markdown.mjs +2 -10
  29. llms/ui/tailwind.input.css +80 -496
  30. llms/ui/threadStore.mjs +572 -0
  31. llms/ui/utils.mjs +117 -113
  32. llms/ui.json +1069 -0
  33. {llms_py-3.0.0.dist-info → llms_py-3.0.0b1.dist-info}/METADATA +1 -1
  34. llms_py-3.0.0b1.dist-info/RECORD +49 -0
  35. llms/__pycache__/__init__.cpython-312.pyc +0 -0
  36. llms/__pycache__/__init__.cpython-313.pyc +0 -0
  37. llms/__pycache__/__init__.cpython-314.pyc +0 -0
  38. llms/__pycache__/__main__.cpython-312.pyc +0 -0
  39. llms/__pycache__/__main__.cpython-314.pyc +0 -0
  40. llms/__pycache__/llms.cpython-312.pyc +0 -0
  41. llms/__pycache__/main.cpython-312.pyc +0 -0
  42. llms/__pycache__/main.cpython-313.pyc +0 -0
  43. llms/__pycache__/main.cpython-314.pyc +0 -0
  44. llms/__pycache__/plugins.cpython-314.pyc +0 -0
  45. llms/extensions/app/README.md +0 -20
  46. llms/extensions/app/__init__.py +0 -530
  47. llms/extensions/app/__pycache__/__init__.cpython-314.pyc +0 -0
  48. llms/extensions/app/__pycache__/db.cpython-314.pyc +0 -0
  49. llms/extensions/app/__pycache__/db_manager.cpython-314.pyc +0 -0
  50. llms/extensions/app/db.py +0 -644
  51. llms/extensions/app/db_manager.py +0 -195
  52. llms/extensions/app/requests.json +0 -9073
  53. llms/extensions/app/threads.json +0 -15290
  54. llms/extensions/app/ui/threadStore.mjs +0 -411
  55. llms/extensions/core_tools/CALCULATOR.md +0 -32
  56. llms/extensions/core_tools/__init__.py +0 -598
  57. llms/extensions/core_tools/__pycache__/__init__.cpython-314.pyc +0 -0
  58. llms/extensions/core_tools/ui/codemirror/addon/edit/closebrackets.js +0 -201
  59. llms/extensions/core_tools/ui/codemirror/addon/edit/closetag.js +0 -185
  60. llms/extensions/core_tools/ui/codemirror/addon/edit/continuelist.js +0 -101
  61. llms/extensions/core_tools/ui/codemirror/addon/edit/matchbrackets.js +0 -160
  62. llms/extensions/core_tools/ui/codemirror/addon/edit/matchtags.js +0 -66
  63. llms/extensions/core_tools/ui/codemirror/addon/edit/trailingspace.js +0 -27
  64. llms/extensions/core_tools/ui/codemirror/addon/selection/active-line.js +0 -72
  65. llms/extensions/core_tools/ui/codemirror/addon/selection/mark-selection.js +0 -119
  66. llms/extensions/core_tools/ui/codemirror/addon/selection/selection-pointer.js +0 -98
  67. llms/extensions/core_tools/ui/codemirror/doc/docs.css +0 -225
  68. llms/extensions/core_tools/ui/codemirror/doc/source_sans.woff +0 -0
  69. llms/extensions/core_tools/ui/codemirror/lib/codemirror.css +0 -344
  70. llms/extensions/core_tools/ui/codemirror/lib/codemirror.js +0 -9884
  71. llms/extensions/core_tools/ui/codemirror/mode/clike/clike.js +0 -942
  72. llms/extensions/core_tools/ui/codemirror/mode/javascript/index.html +0 -118
  73. llms/extensions/core_tools/ui/codemirror/mode/javascript/javascript.js +0 -962
  74. llms/extensions/core_tools/ui/codemirror/mode/javascript/typescript.html +0 -62
  75. llms/extensions/core_tools/ui/codemirror/mode/python/python.js +0 -402
  76. llms/extensions/core_tools/ui/codemirror/theme/dracula.css +0 -40
  77. llms/extensions/core_tools/ui/codemirror/theme/mocha.css +0 -135
  78. llms/extensions/core_tools/ui/index.mjs +0 -650
  79. llms/extensions/gallery/README.md +0 -61
  80. llms/extensions/gallery/__init__.py +0 -61
  81. llms/extensions/gallery/__pycache__/__init__.cpython-314.pyc +0 -0
  82. llms/extensions/gallery/__pycache__/db.cpython-314.pyc +0 -0
  83. llms/extensions/gallery/db.py +0 -298
  84. llms/extensions/gallery/ui/index.mjs +0 -482
  85. llms/extensions/katex/README.md +0 -39
  86. llms/extensions/katex/__init__.py +0 -6
  87. llms/extensions/katex/__pycache__/__init__.cpython-314.pyc +0 -0
  88. llms/extensions/katex/ui/README.md +0 -125
  89. llms/extensions/katex/ui/contrib/auto-render.js +0 -338
  90. llms/extensions/katex/ui/contrib/auto-render.min.js +0 -1
  91. llms/extensions/katex/ui/contrib/auto-render.mjs +0 -244
  92. llms/extensions/katex/ui/contrib/copy-tex.js +0 -127
  93. llms/extensions/katex/ui/contrib/copy-tex.min.js +0 -1
  94. llms/extensions/katex/ui/contrib/copy-tex.mjs +0 -105
  95. llms/extensions/katex/ui/contrib/mathtex-script-type.js +0 -109
  96. llms/extensions/katex/ui/contrib/mathtex-script-type.min.js +0 -1
  97. llms/extensions/katex/ui/contrib/mathtex-script-type.mjs +0 -24
  98. llms/extensions/katex/ui/contrib/mhchem.js +0 -3213
  99. llms/extensions/katex/ui/contrib/mhchem.min.js +0 -1
  100. llms/extensions/katex/ui/contrib/mhchem.mjs +0 -3109
  101. llms/extensions/katex/ui/contrib/render-a11y-string.js +0 -887
  102. llms/extensions/katex/ui/contrib/render-a11y-string.min.js +0 -1
  103. llms/extensions/katex/ui/contrib/render-a11y-string.mjs +0 -800
  104. llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.ttf +0 -0
  105. llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff +0 -0
  106. llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  107. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
  108. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
  109. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  110. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
  111. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
  112. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  113. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
  114. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff +0 -0
  115. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  116. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
  117. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff +0 -0
  118. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  119. llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.ttf +0 -0
  120. llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff +0 -0
  121. llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff2 +0 -0
  122. llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
  123. llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff +0 -0
  124. llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  125. llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.ttf +0 -0
  126. llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff +0 -0
  127. llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff2 +0 -0
  128. llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.ttf +0 -0
  129. llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff +0 -0
  130. llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff2 +0 -0
  131. llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
  132. llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff +0 -0
  133. llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  134. llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.ttf +0 -0
  135. llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff +0 -0
  136. llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff2 +0 -0
  137. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
  138. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff +0 -0
  139. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  140. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
  141. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff +0 -0
  142. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  143. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
  144. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff +0 -0
  145. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  146. llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.ttf +0 -0
  147. llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff +0 -0
  148. llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff2 +0 -0
  149. llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.ttf +0 -0
  150. llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff +0 -0
  151. llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  152. llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.ttf +0 -0
  153. llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff +0 -0
  154. llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  155. llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.ttf +0 -0
  156. llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff +0 -0
  157. llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  158. llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.ttf +0 -0
  159. llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff +0 -0
  160. llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  161. llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
  162. llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff +0 -0
  163. llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  164. llms/extensions/katex/ui/index.mjs +0 -92
  165. llms/extensions/katex/ui/katex-swap.css +0 -1230
  166. llms/extensions/katex/ui/katex-swap.min.css +0 -1
  167. llms/extensions/katex/ui/katex.css +0 -1230
  168. llms/extensions/katex/ui/katex.js +0 -19080
  169. llms/extensions/katex/ui/katex.min.css +0 -1
  170. llms/extensions/katex/ui/katex.min.js +0 -1
  171. llms/extensions/katex/ui/katex.min.mjs +0 -1
  172. llms/extensions/katex/ui/katex.mjs +0 -18547
  173. llms/extensions/providers/__init__.py +0 -18
  174. llms/extensions/providers/__pycache__/__init__.cpython-314.pyc +0 -0
  175. llms/extensions/providers/__pycache__/anthropic.cpython-314.pyc +0 -0
  176. llms/extensions/providers/__pycache__/chutes.cpython-314.pyc +0 -0
  177. llms/extensions/providers/__pycache__/google.cpython-314.pyc +0 -0
  178. llms/extensions/providers/__pycache__/nvidia.cpython-314.pyc +0 -0
  179. llms/extensions/providers/__pycache__/openai.cpython-314.pyc +0 -0
  180. llms/extensions/providers/__pycache__/openrouter.cpython-314.pyc +0 -0
  181. llms/extensions/providers/anthropic.py +0 -229
  182. llms/extensions/providers/chutes.py +0 -155
  183. llms/extensions/providers/google.py +0 -378
  184. llms/extensions/providers/nvidia.py +0 -105
  185. llms/extensions/providers/openai.py +0 -156
  186. llms/extensions/providers/openrouter.py +0 -72
  187. llms/extensions/system_prompts/README.md +0 -22
  188. llms/extensions/system_prompts/__init__.py +0 -45
  189. llms/extensions/system_prompts/__pycache__/__init__.cpython-314.pyc +0 -0
  190. llms/extensions/system_prompts/ui/index.mjs +0 -280
  191. llms/extensions/system_prompts/ui/prompts.json +0 -1067
  192. llms/extensions/tools/__init__.py +0 -5
  193. llms/extensions/tools/__pycache__/__init__.cpython-314.pyc +0 -0
  194. llms/extensions/tools/ui/index.mjs +0 -204
  195. llms/providers-extra.json +0 -356
  196. llms/ui/ctx.mjs +0 -365
  197. llms/ui/index.mjs +0 -129
  198. llms/ui/modules/chat/ChatBody.mjs +0 -691
  199. llms/ui/modules/chat/index.mjs +0 -828
  200. llms/ui/modules/layout.mjs +0 -243
  201. llms/ui/modules/model-selector.mjs +0 -851
  202. llms_py-3.0.0.dist-info/RECORD +0 -202
  203. {llms_py-3.0.0.dist-info → llms_py-3.0.0b1.dist-info}/WHEEL +0 -0
  204. {llms_py-3.0.0.dist-info → llms_py-3.0.0b1.dist-info}/entry_points.txt +0 -0
  205. {llms_py-3.0.0.dist-info → llms_py-3.0.0b1.dist-info}/licenses/LICENSE +0 -0
  206. {llms_py-3.0.0.dist-info → llms_py-3.0.0b1.dist-info}/top_level.txt +0 -0
llms/ui/App.mjs CHANGED
@@ -1,188 +1,97 @@
1
- import { ref, computed, watch, inject, onMounted, onUnmounted } from "vue"
2
- import { useRouter, useRoute } from "vue-router"
3
- import { AppContext } from "./ctx.mjs"
4
-
5
- // Vertical Sidebar Icons
6
- const LeftBar = {
7
- template: `
8
- <div class="select-none flex flex-col space-y-2 pt-2.5 px-1">
9
- <div v-for="(icon, id) in $ctx.left" :key="id" class="relative flex items-center justify-center">
10
- <component :is="icon.component"
11
- class="size-7 p-1 cursor-pointer text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 rounded block"
12
- :class="{ 'bg-gray-200 dark:bg-gray-700' : icon.isActive({ ...$layout }) }"
13
- @mouseenter="tooltip = icon.id"
14
- @mouseleave="tooltip = ''"
15
- />
16
- <div v-if="tooltip === icon.id && !icon.isActive({ ...$layout })"
17
- class="absolute left-full top-1/2 -translate-y-1/2 ml-2 px-2 py-1 text-xs text-white bg-gray-900 dark:bg-gray-800 rounded shadow-md z-50 whitespace-nowrap pointer-events-none" style="z-index: 60">
18
- {{icon.title ?? icon.name}}
19
- </div>
20
- </div>
21
- </div>
22
- `,
23
- setup() {
24
- const tooltip = ref('')
25
- return {
26
- tooltip,
27
- }
28
- }
29
- }
30
-
31
- const LeftPanel = {
32
- template: `
33
- <div v-if="component" class="flex flex-col h-full border-r border-gray-200 dark:border-gray-700">
34
- <button type="button" class="absolute top-2 right-2 p-1 rounded-md text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-700 lg:hidden z-20">
35
- <svg class="size-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path 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>
36
- </button>
37
- <component :is="component" />
38
- </div>
39
- `,
40
- setup() {
41
- /**@type {AppContext} */
42
- const ctx = inject('ctx')
43
- const component = computed(() => ctx.component(ctx.layout.left))
44
- return {
45
- component,
46
- }
47
- }
48
- }
49
-
50
- const TopBar = {
51
- template: `
52
- <div class="select-none flex space-x-1">
53
- <div v-for="(icon, id) in $ctx.top" :key="id" class="relative flex items-center justify-center">
54
- <component :is="icon.component"
55
- class="size-7 p-1 cursor-pointer text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 block border border-transparent"
56
- :class="{ 'bg-gray-100 dark:bg-gray-800 border-gray-300 dark:border-gray-600 rounded' : icon.isActive({ ...$layout }) }"
57
- @mouseenter="tooltip = icon.id"
58
- @mouseleave="tooltip = ''"
59
- />
60
- <div v-if="tooltip === icon.id && !icon.isActive({ ...$layout })"
61
- class="absolute top-full mt-2 px-2 py-1 text-xs text-white bg-gray-900 dark:bg-gray-800 rounded shadow-md z-50 whitespace-nowrap pointer-events-none"
62
- :class="last2.includes(id) ? 'right-0' : 'left-1/2 -translate-x-1/2'">
63
- {{icon.title ?? icon.name}}
64
- </div>
65
- </div>
66
- </div>
67
- `,
68
- setup() {
69
- const tooltip = ref('')
70
- const last2 = ref(Object.keys($ctx.top).slice(-2))
71
- return {
72
- tooltip,
73
- last2,
74
- }
75
- }
76
- }
77
-
78
- const TopPanel = {
79
- template: `
80
- <component v-if="component" :is="component" class="mb-2" />
81
- `,
82
- setup() {
83
- /**@type {AppContext} */
84
- const ctx = inject('ctx')
85
- const component = computed(() => ctx.component(ctx.layout.top))
86
- return {
87
- component,
88
- }
89
- }
90
- }
1
+ import { inject, ref, onMounted, onUnmounted } from "vue"
2
+ import Sidebar from "./Sidebar.mjs"
91
3
 
92
4
  export default {
93
5
  components: {
94
- LeftBar,
95
- LeftPanel,
96
- TopBar,
97
- TopPanel,
6
+ Sidebar,
98
7
  },
99
8
  setup() {
100
- const router = useRouter()
101
- const route = useRoute()
102
-
103
- /**@type {AppContext} */
104
- const ctx = inject('ctx')
105
- const ai = ctx.ai
9
+ const ai = inject('ai')
106
10
  const isMobile = ref(false)
107
- const modal = ref()
108
11
 
109
12
  const checkMobile = () => {
110
- //const wasMobile = isMobile.value
111
- isMobile.value = window.innerWidth < 640 // sm breakpoint
13
+ const wasMobile = isMobile.value
14
+ isMobile.value = window.innerWidth < 1024 // lg breakpoint
112
15
 
113
- //console.log('checkMobile', wasMobile, isMobile.value)
114
16
  // Only auto-adjust sidebar state when transitioning between mobile/desktop
17
+ if (wasMobile !== isMobile.value) {
18
+ if (isMobile.value) {
19
+ ai.isSidebarOpen = false
20
+ } else {
21
+ ai.isSidebarOpen = true
22
+ }
23
+ }
24
+ }
25
+
26
+ const toggleSidebar = () => {
27
+ ai.isSidebarOpen = !ai.isSidebarOpen
28
+ }
29
+
30
+ const closeSidebar = () => {
115
31
  if (isMobile.value) {
116
- ctx.toggleLayout('left', false)
32
+ ai.isSidebarOpen = false
117
33
  }
118
34
  }
119
35
 
120
36
  onMounted(() => {
121
37
  checkMobile()
122
38
  window.addEventListener('resize', checkMobile)
123
- if (route.query.open) {
124
- modal.value = ctx.openModal(route.query.open)
125
- }
126
39
  })
127
40
 
128
41
  onUnmounted(() => {
129
42
  window.removeEventListener('resize', checkMobile)
130
43
  })
131
44
 
132
- function closeModal() {
133
- ctx.closeModal(route.query.open)
134
- }
135
-
136
- watch(() => route.query.open, (newVal) => {
137
- modal.value = ctx.modalComponents[newVal]
138
- console.log('open', newVal, modal.value)
139
- })
140
-
141
- watch(() => ctx.state.selectedModel, (newVal) => {
142
- ctx.chat.setSelectedModel(ctx.chat.getModel(newVal))
143
- })
144
-
145
- return { ai, modal, isMobile, closeModal }
45
+ return { ai, isMobile, toggleSidebar, closeSidebar }
146
46
  },
147
47
  template: `
148
- <div class="flex h-screen">
48
+ <div class="flex h-screen bg-white dark:bg-gray-900">
149
49
  <!-- Mobile Overlay -->
150
- <div v-if="isMobile && $ctx.layoutVisible('left') && $ai.hasAccess"
151
- @click="$ctx.toggleLayout('left')"
152
- :class="$ctx.cls('mobile-overlay', 'fixed inset-0 bg-black/50 z-40 lg:hidden')"
50
+ <div
51
+ v-if="isMobile && ai.isSidebarOpen && !(ai.requiresAuth && !ai.auth)"
52
+ @click="closeSidebar"
53
+ class="fixed inset-0 bg-black/50 z-40 lg:hidden"
153
54
  ></div>
154
55
 
155
- <div v-if="$ai.hasAccess" id="sidebar" :class="$ctx.cls('sidebar', 'z-100 relative flex bg-gray-50 dark:bg-gray-800')">
156
- <LeftBar id="left-bar" />
157
- <LeftPanel id="left-panel"
158
- v-if="$ai.hasAccess && $ctx.layoutVisible('left')"
159
- :class="[
160
- 'transition-transform duration-300 ease-in-out z-50',
161
- 'w-72 xl:w-80 flex-shrink-0',
162
- 'lg:relative',
163
- 'fixed inset-y-0 left-[2.25rem] lg:left-0',
164
- 'bg-gray-50 dark:bg-gray-800'
165
- ]"
166
- />
56
+ <!-- Sidebar (hidden when auth required and not authenticated) -->
57
+ <div
58
+ v-if="!(ai.requiresAuth && !ai.auth) && ai.isSidebarOpen"
59
+ :class="[
60
+ 'transition-transform duration-300 ease-in-out z-50',
61
+ 'w-72 xl:w-80 flex-shrink-0',
62
+ 'lg:relative',
63
+ 'fixed inset-y-0 left-0'
64
+ ]"
65
+ >
66
+ <Sidebar @thread-selected="closeSidebar" @toggle-sidebar="toggleSidebar" />
167
67
  </div>
168
68
 
169
69
  <!-- Main Area -->
170
- <div id="main" :class="$ctx.cls('main', 'flex-1 flex flex-col')">
171
- <div id="main-inner" :class="$ctx.cls('main-inner', 'flex flex-col h-full w-full overflow-hidden')">
172
- <div v-if="$ai.hasAccess" id="header" :class="$ctx.cls('header', 'py-1 pr-1 flex items-center justify-between shrink-0')">
173
- <div>
174
- <ModelSelector :models="$state.models" v-model="$state.selectedModel" />
70
+ <div class="flex-1 flex flex-col">
71
+ <!-- Collapsed Sidebar Toggle Button -->
72
+ <div
73
+ v-if="!(ai.requiresAuth && !ai.auth) && !ai.isSidebarOpen"
74
+ class="fixed top-4 left-0"
75
+ >
76
+ <button type="button"
77
+ @click="toggleSidebar"
78
+ class="group p-1 text-gray-500 dark:text-gray-400 hover:text-blue-600 dark:hover:text-blue-400 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
79
+ title="Open sidebar"
80
+ >
81
+ <div class="relative w-5 h-5">
82
+ <!-- Default sidebar icon -->
83
+ <svg class="absolute inset-0 group-hover:hidden" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
84
+ <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
85
+ <line x1="9" y1="3" x2="9" y2="21"></line>
86
+ </svg>
87
+ <!-- Hover state: |→ icon -->
88
+ <svg class="absolute inset-0 hidden group-hover:block" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="m17.172 11l-4.657-4.657l1.414-1.414L21 12l-7.071 7.071l-1.414-1.414L17.172 13H8v-2zM4 19V5h2v14z"/></svg>
175
89
  </div>
176
- <TopBar id="top-bar" />
177
- </div>
178
- <TopPanel v-if="$ai.hasAccess" id="top-panel" :class="$ctx.cls('top-panel', 'shrink-0')" />
179
- <div id="page" :class="$ctx.cls('page', 'flex-1 overflow-y-auto min-h-0 flex flex-col')">
180
- <RouterView class="h-full" />
181
- </div>
90
+ </button>
182
91
  </div>
183
- </div>
184
92
 
185
- <component v-if="modal" :is="modal" :class="$ctx.cls('modal', '!z-[200]')" @done="closeModal" />
93
+ <RouterView />
94
+ </div>
186
95
  </div>
187
96
  `,
188
97
  }
llms/ui/Avatar.mjs ADDED
@@ -0,0 +1,85 @@
1
+ import { computed, inject, ref, onMounted, onUnmounted } from "vue"
2
+
3
+ export default {
4
+ template:`
5
+ <div v-if="$ai.auth?.profileUrl" class="relative" ref="avatarContainer">
6
+ <img
7
+ @click.stop="toggleMenu"
8
+ :src="$ai.auth.profileUrl"
9
+ :title="authTitle"
10
+ class="size-8 rounded-full cursor-pointer hover:ring-2 hover:ring-gray-300"
11
+ />
12
+ <div
13
+ v-if="showMenu"
14
+ @click.stop
15
+ class="absolute right-0 mt-2 w-48 bg-white dark:bg-gray-800 rounded-md shadow-lg py-1 z-50 border border-gray-200 dark:border-gray-700"
16
+ >
17
+ <div class="px-4 py-2 text-sm text-gray-700 dark:text-gray-300 border-b border-gray-200 dark:border-gray-700">
18
+ <div class="font-medium whitespace-nowrap overflow-hidden text-ellipsis">{{ $ai.auth.displayName || $ai.auth.userName }}</div>
19
+ <div class="text-xs text-gray-500 dark:text-gray-400 whitespace-nowrap overflow-hidden text-ellipsis">{{ $ai.auth.email }}</div>
20
+ </div>
21
+ <button type="button"
22
+ @click="handleLogout"
23
+ class="w-full text-left px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 flex items-center whitespace-nowrap"
24
+ >
25
+ <svg class="w-4 h-4 mr-2 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
26
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"></path>
27
+ </svg>
28
+ Sign Out
29
+ </button>
30
+ </div>
31
+ </div>
32
+ `,
33
+ setup() {
34
+ const ai = inject('ai')
35
+ const showMenu = ref(false)
36
+ const avatarContainer = ref(null)
37
+
38
+ const authTitle = computed(() => {
39
+ if (!ai.auth) return ''
40
+ const { userId, userName, displayName, bearerToken, roles } = ai.auth
41
+ const name = userName || displayName
42
+ const prefix = roles && roles.includes('Admin') ? 'Admin' : 'Name'
43
+ const sb = [
44
+ name ? `${prefix}: ${name}` : '',
45
+ `API Key: ${bearerToken}`,
46
+ `${userId}`,
47
+ ]
48
+ return sb.filter(x => x).join('\n')
49
+ })
50
+
51
+ function toggleMenu() {
52
+ showMenu.value = !showMenu.value
53
+ }
54
+
55
+ async function handleLogout() {
56
+ showMenu.value = false
57
+ await ai.signOut()
58
+ // Reload the page to show sign-in screen
59
+ window.location.reload()
60
+ }
61
+
62
+ // Close menu when clicking outside
63
+ const handleClickOutside = (event) => {
64
+ if (showMenu.value && avatarContainer.value && !avatarContainer.value.contains(event.target)) {
65
+ showMenu.value = false
66
+ }
67
+ }
68
+
69
+ onMounted(() => {
70
+ document.addEventListener('click', handleClickOutside)
71
+ })
72
+
73
+ onUnmounted(() => {
74
+ document.removeEventListener('click', handleClickOutside)
75
+ })
76
+
77
+ return {
78
+ authTitle,
79
+ handleLogout,
80
+ showMenu,
81
+ toggleMenu,
82
+ avatarContainer,
83
+ }
84
+ }
85
+ }
llms/ui/Brand.mjs ADDED
@@ -0,0 +1,52 @@
1
+ export default {
2
+ template:`
3
+ <div class="flex-shrink-0 pl-2 pr-4 py-4 border-b border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 min-h-16 select-none">
4
+ <div class="flex items-center justify-between">
5
+ <div class="flex items-center space-x-2">
6
+ <button type="button"
7
+ @click="$emit('toggle-sidebar')"
8
+ class="group relative text-gray-500 dark:text-gray-400 hover:text-blue-600 dark:hover:text-blue-400 focus:outline-none transition-colors"
9
+ title="Collapse sidebar"
10
+ >
11
+ <div class="relative size-5">
12
+ <!-- Default sidebar icon -->
13
+ <svg class="absolute inset-0 group-hover:hidden" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
14
+ <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
15
+ <line x1="9" y1="3" x2="9" y2="21"></line>
16
+ </svg>
17
+ <!-- Hover state: |← icon -->
18
+ <svg class="absolute inset-0 hidden group-hover:block" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="m10.071 4.929l1.414 1.414L6.828 11H16v2H6.828l4.657 4.657l-1.414 1.414L3 12zM18.001 19V5h2v14z"/></svg>
19
+ </div>
20
+ </button>
21
+
22
+ <button type="button"
23
+ @click="$emit('home')"
24
+ class="text-lg font-semibold text-gray-900 dark:text-gray-200 hover:text-blue-600 dark:hover:text-blue-400 focus:outline-none transition-colors"
25
+ title="Go back to initial state"
26
+ >
27
+ History
28
+ </button>
29
+ </div>
30
+
31
+ <div class="flex items-center space-x-2">
32
+ <button type="button"
33
+ @click="$emit('analytics')"
34
+ class="text-gray-900 dark:text-gray-200 hover:text-blue-600 dark:hover:text-blue-400 focus:outline-none transition-colors"
35
+ title="Analytics"
36
+ >
37
+ <svg class="size-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M5 22a1 1 0 0 1-1-1v-8a1 1 0 0 1 2 0v8a1 1 0 0 1-1 1m5 0a1 1 0 0 1-1-1V3a1 1 0 0 1 2 0v18a1 1 0 0 1-1 1m5 0a1 1 0 0 1-1-1V9a1 1 0 0 1 2 0v12a1 1 0 0 1-1 1m5 0a1 1 0 0 1-1-1v-4a1 1 0 0 1 2 0v4a1 1 0 0 1-1 1"/></svg>
38
+ </button>
39
+
40
+ <button type="button"
41
+ @click="$emit('new')"
42
+ class="text-gray-900 dark:text-gray-200 hover:text-blue-600 dark:hover:text-blue-400 focus:outline-none transition-colors"
43
+ title="New Chat"
44
+ >
45
+ <svg class="size-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.375 2.625a1 1 0 0 1 3 3l-9.013 9.014a2 2 0 0 1-.853.505l-2.873.84a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.852z"/></g></svg>
46
+ </button>
47
+ </div>
48
+ </div>
49
+ </div>
50
+ `,
51
+ emits:['home','new','analytics','toggle-sidebar'],
52
+ }