llms-py 2.0.9__py3-none-any.whl → 3.0.10__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.
- llms/__init__.py +4 -0
- llms/__main__.py +9 -0
- llms/db.py +359 -0
- llms/extensions/analytics/ui/index.mjs +1444 -0
- llms/extensions/app/README.md +20 -0
- llms/extensions/app/__init__.py +589 -0
- llms/extensions/app/db.py +536 -0
- {llms_py-2.0.9.data/data → llms/extensions/app}/ui/Recents.mjs +100 -73
- llms_py-2.0.9.data/data/ui/Sidebar.mjs → llms/extensions/app/ui/index.mjs +150 -79
- llms/extensions/app/ui/threadStore.mjs +433 -0
- llms/extensions/core_tools/CALCULATOR.md +32 -0
- llms/extensions/core_tools/__init__.py +637 -0
- llms/extensions/core_tools/ui/codemirror/addon/edit/closebrackets.js +201 -0
- llms/extensions/core_tools/ui/codemirror/addon/edit/closetag.js +185 -0
- llms/extensions/core_tools/ui/codemirror/addon/edit/continuelist.js +101 -0
- llms/extensions/core_tools/ui/codemirror/addon/edit/matchbrackets.js +160 -0
- llms/extensions/core_tools/ui/codemirror/addon/edit/matchtags.js +66 -0
- llms/extensions/core_tools/ui/codemirror/addon/edit/trailingspace.js +27 -0
- llms/extensions/core_tools/ui/codemirror/addon/selection/active-line.js +72 -0
- llms/extensions/core_tools/ui/codemirror/addon/selection/mark-selection.js +119 -0
- llms/extensions/core_tools/ui/codemirror/addon/selection/selection-pointer.js +98 -0
- llms/extensions/core_tools/ui/codemirror/codemirror.css +344 -0
- llms/extensions/core_tools/ui/codemirror/codemirror.js +9884 -0
- llms/extensions/core_tools/ui/codemirror/doc/docs.css +225 -0
- llms/extensions/core_tools/ui/codemirror/doc/source_sans.woff +0 -0
- llms/extensions/core_tools/ui/codemirror/mode/clike/clike.js +942 -0
- llms/extensions/core_tools/ui/codemirror/mode/javascript/index.html +118 -0
- llms/extensions/core_tools/ui/codemirror/mode/javascript/javascript.js +962 -0
- llms/extensions/core_tools/ui/codemirror/mode/javascript/typescript.html +62 -0
- llms/extensions/core_tools/ui/codemirror/mode/python/python.js +402 -0
- llms/extensions/core_tools/ui/codemirror/theme/dracula.css +40 -0
- llms/extensions/core_tools/ui/codemirror/theme/mocha.css +135 -0
- llms/extensions/core_tools/ui/index.mjs +650 -0
- llms/extensions/gallery/README.md +61 -0
- llms/extensions/gallery/__init__.py +63 -0
- llms/extensions/gallery/db.py +243 -0
- llms/extensions/gallery/ui/index.mjs +482 -0
- llms/extensions/katex/README.md +39 -0
- llms/extensions/katex/__init__.py +6 -0
- llms/extensions/katex/ui/README.md +125 -0
- llms/extensions/katex/ui/contrib/auto-render.js +338 -0
- llms/extensions/katex/ui/contrib/auto-render.min.js +1 -0
- llms/extensions/katex/ui/contrib/auto-render.mjs +244 -0
- llms/extensions/katex/ui/contrib/copy-tex.js +127 -0
- llms/extensions/katex/ui/contrib/copy-tex.min.js +1 -0
- llms/extensions/katex/ui/contrib/copy-tex.mjs +105 -0
- llms/extensions/katex/ui/contrib/mathtex-script-type.js +109 -0
- llms/extensions/katex/ui/contrib/mathtex-script-type.min.js +1 -0
- llms/extensions/katex/ui/contrib/mathtex-script-type.mjs +24 -0
- llms/extensions/katex/ui/contrib/mhchem.js +3213 -0
- llms/extensions/katex/ui/contrib/mhchem.min.js +1 -0
- llms/extensions/katex/ui/contrib/mhchem.mjs +3109 -0
- llms/extensions/katex/ui/contrib/render-a11y-string.js +887 -0
- llms/extensions/katex/ui/contrib/render-a11y-string.min.js +1 -0
- llms/extensions/katex/ui/contrib/render-a11y-string.mjs +800 -0
- llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- llms/extensions/katex/ui/index.mjs +92 -0
- llms/extensions/katex/ui/katex-swap.css +1230 -0
- llms/extensions/katex/ui/katex-swap.min.css +1 -0
- llms/extensions/katex/ui/katex.css +1230 -0
- llms/extensions/katex/ui/katex.js +19080 -0
- llms/extensions/katex/ui/katex.min.css +1 -0
- llms/extensions/katex/ui/katex.min.js +1 -0
- llms/extensions/katex/ui/katex.min.mjs +1 -0
- llms/extensions/katex/ui/katex.mjs +18547 -0
- llms/extensions/providers/__init__.py +22 -0
- llms/extensions/providers/anthropic.py +233 -0
- llms/extensions/providers/cerebras.py +37 -0
- llms/extensions/providers/chutes.py +153 -0
- llms/extensions/providers/google.py +481 -0
- llms/extensions/providers/nvidia.py +103 -0
- llms/extensions/providers/openai.py +154 -0
- llms/extensions/providers/openrouter.py +74 -0
- llms/extensions/providers/zai.py +182 -0
- llms/extensions/system_prompts/README.md +22 -0
- llms/extensions/system_prompts/__init__.py +45 -0
- llms/extensions/system_prompts/ui/index.mjs +280 -0
- llms/extensions/system_prompts/ui/prompts.json +1067 -0
- llms/extensions/tools/__init__.py +144 -0
- llms/extensions/tools/ui/index.mjs +706 -0
- llms/index.html +58 -0
- llms/llms.json +400 -0
- llms/main.py +4407 -0
- llms/providers-extra.json +394 -0
- llms/providers.json +1 -0
- llms/ui/App.mjs +188 -0
- llms/ui/ai.mjs +217 -0
- llms/ui/app.css +7081 -0
- llms/ui/ctx.mjs +412 -0
- llms/ui/index.mjs +131 -0
- llms/ui/lib/chart.js +14 -0
- llms/ui/lib/charts.mjs +16 -0
- llms/ui/lib/color.js +14 -0
- llms/ui/lib/servicestack-vue.mjs +37 -0
- llms/ui/lib/vue.min.mjs +13 -0
- llms/ui/lib/vue.mjs +18530 -0
- {llms_py-2.0.9.data/data → llms}/ui/markdown.mjs +33 -15
- llms/ui/modules/chat/ChatBody.mjs +976 -0
- llms/ui/modules/chat/SettingsDialog.mjs +374 -0
- llms/ui/modules/chat/index.mjs +991 -0
- llms/ui/modules/icons.mjs +46 -0
- llms/ui/modules/layout.mjs +271 -0
- llms/ui/modules/model-selector.mjs +811 -0
- llms/ui/tailwind.input.css +742 -0
- {llms_py-2.0.9.data/data → llms}/ui/typography.css +133 -7
- llms/ui/utils.mjs +261 -0
- llms_py-3.0.10.dist-info/METADATA +49 -0
- llms_py-3.0.10.dist-info/RECORD +177 -0
- llms_py-3.0.10.dist-info/entry_points.txt +2 -0
- {llms_py-2.0.9.dist-info → llms_py-3.0.10.dist-info}/licenses/LICENSE +1 -2
- llms.py +0 -1402
- llms_py-2.0.9.data/data/index.html +0 -64
- llms_py-2.0.9.data/data/llms.json +0 -447
- llms_py-2.0.9.data/data/requirements.txt +0 -1
- llms_py-2.0.9.data/data/ui/App.mjs +0 -20
- llms_py-2.0.9.data/data/ui/ChatPrompt.mjs +0 -389
- llms_py-2.0.9.data/data/ui/Main.mjs +0 -680
- llms_py-2.0.9.data/data/ui/app.css +0 -3951
- llms_py-2.0.9.data/data/ui/lib/servicestack-vue.min.mjs +0 -37
- llms_py-2.0.9.data/data/ui/lib/vue.min.mjs +0 -12
- llms_py-2.0.9.data/data/ui/tailwind.input.css +0 -261
- llms_py-2.0.9.data/data/ui/threadStore.mjs +0 -273
- llms_py-2.0.9.data/data/ui/utils.mjs +0 -114
- llms_py-2.0.9.data/data/ui.json +0 -1069
- llms_py-2.0.9.dist-info/METADATA +0 -941
- llms_py-2.0.9.dist-info/RECORD +0 -30
- llms_py-2.0.9.dist-info/entry_points.txt +0 -2
- {llms_py-2.0.9.data/data → llms}/ui/fav.svg +0 -0
- {llms_py-2.0.9.data/data → llms}/ui/lib/highlight.min.mjs +0 -0
- {llms_py-2.0.9.data/data → llms}/ui/lib/idb.min.mjs +0 -0
- {llms_py-2.0.9.data/data → llms}/ui/lib/marked.min.mjs +0 -0
- /llms_py-2.0.9.data/data/ui/lib/servicestack-client.min.mjs → /llms/ui/lib/servicestack-client.mjs +0 -0
- {llms_py-2.0.9.data/data → llms}/ui/lib/vue-router.min.mjs +0 -0
- {llms_py-2.0.9.dist-info → llms_py-3.0.10.dist-info}/WHEEL +0 -0
- {llms_py-2.0.9.dist-info → llms_py-3.0.10.dist-info}/top_level.txt +0 -0
|
@@ -1,261 +0,0 @@
|
|
|
1
|
-
/* tailwindcss -i ./tailwind.input.css -o ./ui/app.css --watch */
|
|
2
|
-
@import "tailwindcss";
|
|
3
|
-
@source "./lib/servicestack-vue.min.mjs";
|
|
4
|
-
|
|
5
|
-
@custom-variant dark (&:where(.dark, .dark *));
|
|
6
|
-
|
|
7
|
-
@layer base {
|
|
8
|
-
:root {
|
|
9
|
-
--background: 0 0% 100%;
|
|
10
|
-
--foreground: 222.2 84% 4.9%;
|
|
11
|
-
--border: 214.3 31.8% 91.4%;
|
|
12
|
-
--input: 214.3 31.8% 91.4%;
|
|
13
|
-
--ring: 221.2 83.2% 53.3%;
|
|
14
|
-
--radius: 0.5rem;
|
|
15
|
-
}
|
|
16
|
-
:root.dark {
|
|
17
|
-
--background: 222.2 84% 4.9%;
|
|
18
|
-
--foreground: 210 40% 98%;
|
|
19
|
-
--border: 217.2 32.6% 17.5%;
|
|
20
|
-
--input: 217.2 32.6% 17.5%;
|
|
21
|
-
--ring: 212.7 26.8% 83.9%;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
*,
|
|
25
|
-
::after,
|
|
26
|
-
::before,
|
|
27
|
-
::backdrop,
|
|
28
|
-
::file-selector-button {
|
|
29
|
-
border-color: hsl(var(--border));
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
@theme {
|
|
34
|
-
--default-ring-color: hsl(var(--ring));
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
@layer base {
|
|
38
|
-
|
|
39
|
-
/*vue*/
|
|
40
|
-
[v-cloak] {display:none}
|
|
41
|
-
|
|
42
|
-
/* override element defaults */
|
|
43
|
-
b, strong { font-weight:600; }
|
|
44
|
-
::-webkit-scrollbar{width:8px;height:8px}
|
|
45
|
-
::-webkit-scrollbar-thumb{background-color:#ccc}
|
|
46
|
-
[role=dialog].z-10 {
|
|
47
|
-
z-index: 60;
|
|
48
|
-
}
|
|
49
|
-
em {
|
|
50
|
-
color: #3b82f6;
|
|
51
|
-
font-weight: 400;
|
|
52
|
-
background-color: #eff6ff;
|
|
53
|
-
border-radius: 0.25rem;
|
|
54
|
-
padding: 0.125em 0.5rem;
|
|
55
|
-
margin-left: 0.125em;
|
|
56
|
-
margin-right: 0.125em;
|
|
57
|
-
font-style: normal;
|
|
58
|
-
}
|
|
59
|
-
.message em {
|
|
60
|
-
background-color: #eff6ff;border:1px solid #3b83f680;border-radius: .25rem;padding: .2rem .25rem .1rem .25rem
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/* Chat-specific styles */
|
|
64
|
-
.prose {
|
|
65
|
-
max-width: none;
|
|
66
|
-
}
|
|
67
|
-
.prose > pre {
|
|
68
|
-
margin-top: 0;
|
|
69
|
-
margin-bottom: 0;
|
|
70
|
-
}
|
|
71
|
-
.prose pre {
|
|
72
|
-
background-color: #1f2937;
|
|
73
|
-
color: #f9fafb;
|
|
74
|
-
border-radius: 0.5rem;
|
|
75
|
-
padding: 1rem;
|
|
76
|
-
overflow-x: auto;
|
|
77
|
-
}
|
|
78
|
-
.prose code {
|
|
79
|
-
background-color: #f3f4f6;
|
|
80
|
-
color: #1f2937;
|
|
81
|
-
padding: 0.125rem 0.25rem;
|
|
82
|
-
border-radius: 0.25rem;
|
|
83
|
-
font-size: 0.875em;
|
|
84
|
-
}
|
|
85
|
-
.prose pre code {
|
|
86
|
-
background-color: transparent;
|
|
87
|
-
color: inherit;
|
|
88
|
-
padding: 0;
|
|
89
|
-
}
|
|
90
|
-
.prose blockquote {
|
|
91
|
-
border-left: 4px solid #e5e7eb;
|
|
92
|
-
padding-left: 1rem;
|
|
93
|
-
font-style: italic;
|
|
94
|
-
color: #6b7280;
|
|
95
|
-
}
|
|
96
|
-
.prose table {
|
|
97
|
-
border-collapse: collapse;
|
|
98
|
-
width: 100%;
|
|
99
|
-
}
|
|
100
|
-
.prose th,
|
|
101
|
-
.prose td {
|
|
102
|
-
border: 1px solid #e5e7eb;
|
|
103
|
-
padding: 0.5rem;
|
|
104
|
-
text-align: left;
|
|
105
|
-
}
|
|
106
|
-
.prose th {
|
|
107
|
-
background-color: #f9fafb;
|
|
108
|
-
font-weight: 600;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/* highlight.js - vs.css */
|
|
112
|
-
.hljs {background:white;color:black}
|
|
113
|
-
.hljs-comment,.hljs-quote,.hljs-variable{color:#008000}
|
|
114
|
-
.hljs-keyword,.hljs-selector-tag,.hljs-built_in,.hljs-name,.hljs-tag{color:#00f}
|
|
115
|
-
.hljs-string,.hljs-title,.hljs-section,.hljs-attribute,.hljs-literal,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-addition{color:#a31515}
|
|
116
|
-
.hljs-deletion,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-meta{color:#2b91af}
|
|
117
|
-
.hljs-doctag{color:#808080}
|
|
118
|
-
.hljs-attr{color: #f00}
|
|
119
|
-
.hljs-symbol,.hljs-bullet,.hljs-link{color:#00b0e8}
|
|
120
|
-
.hljs-emphasis{font-style:italic}
|
|
121
|
-
.hljs-strong{font-weight:bold}
|
|
122
|
-
|
|
123
|
-
/* https://unpkg.com/@highlightjs/cdn-assets/styles/atom-one-dark.min.css */
|
|
124
|
-
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#abb2bf;background:#282c34}
|
|
125
|
-
.hljs-comment,.hljs-quote{color:#5c6370;font-style:italic}.hljs-doctag,.hljs-formula,.hljs-keyword{color:#c678dd}
|
|
126
|
-
.hljs-deletion,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-subst,.hljs-tag{color:#e06c75}
|
|
127
|
-
.hljs-literal{color:#56b6c2}
|
|
128
|
-
.hljs-addition,.hljs-attribute,.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#98c379}
|
|
129
|
-
.hljs-attr,.hljs-number,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-pseudo,.hljs-template-variable,.hljs-type,.hljs-variable{color:#d19a66}
|
|
130
|
-
.hljs-bullet,.hljs-link,.hljs-meta,.hljs-selector-id,.hljs-symbol,.hljs-title{color:#61aeee}
|
|
131
|
-
.hljs-built_in,.hljs-class .hljs-title,.hljs-title.class_{color:#e6c07b}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
|
132
|
-
.hljs-link{text-decoration:underline}
|
|
133
|
-
|
|
134
|
-
/*highlightjs*/
|
|
135
|
-
.hljs, .prose :where(pre):not(:where([class~="not-prose"] *)) .hljs {
|
|
136
|
-
color: #e5e7eb !important;
|
|
137
|
-
background-color: #282c34 !important;
|
|
138
|
-
}
|
|
139
|
-
.hljs-comment, .hljs-quote {
|
|
140
|
-
color: rgb(148 163 184); /*text-slate-400*/
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
pre {
|
|
144
|
-
overflow-x: auto;
|
|
145
|
-
font-weight: 400;
|
|
146
|
-
font-size: .875em;
|
|
147
|
-
line-height: 1.7142857;
|
|
148
|
-
margin-top: 1.7142857em;
|
|
149
|
-
margin-bottom: 1.7142857em;
|
|
150
|
-
border-radius: .375rem;
|
|
151
|
-
padding: .8571429em 1.1428571em;
|
|
152
|
-
max-width: calc(100vw - 1rem);
|
|
153
|
-
min-width: fit-content;
|
|
154
|
-
background-color: #282c34 !important;
|
|
155
|
-
}
|
|
156
|
-
pre code.hljs {
|
|
157
|
-
display: block;
|
|
158
|
-
overflow-x: auto;
|
|
159
|
-
padding: 1em;
|
|
160
|
-
}
|
|
161
|
-
.message pre {
|
|
162
|
-
max-width: 100%;
|
|
163
|
-
min-width: auto;
|
|
164
|
-
}
|
|
165
|
-
.message pre code.hljs {
|
|
166
|
-
overflow-x: unset;
|
|
167
|
-
width: 100%;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/* Smooth transitions */
|
|
171
|
-
.transition-all {
|
|
172
|
-
transition-property: all;
|
|
173
|
-
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
|
174
|
-
transition-duration: 150ms;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/* Focus styles */
|
|
178
|
-
.focus-ring:focus {
|
|
179
|
-
outline: 2px solid transparent;
|
|
180
|
-
outline-offset: 2px;
|
|
181
|
-
box-shadow: 0 0 0 2px #3b82f6;
|
|
182
|
-
}
|
|
183
|
-
/* @tailwindcss/forms css */
|
|
184
|
-
[type='text'],[type='email'],[type='url'],[type='password'],[type='number'],[type='date'],[type='datetime-local'],[type='month'],[type='week'],[type='search'],[type='tel'],[type='time'],[type='color'],[multiple],textarea,select
|
|
185
|
-
{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-width:1px;padding:0.5rem 0.75rem;font-size:1rem}
|
|
186
|
-
[type='text']:focus,[type='email']:focus,[type='url']:focus,[type='password']:focus,[type='number']:focus,[type='date']:focus,[type='datetime-local']:focus,[type='month']:focus,[type='week']:focus,[type='search']:focus,[type='tel']:focus,[type='time']:focus,[type='color']:focus,[multiple]:focus,textarea:focus,select:focus{
|
|
187
|
-
outline:2px solid transparent;outline-offset:2px;--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);
|
|
188
|
-
--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#2563eb;
|
|
189
|
-
--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
|
|
190
|
-
--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);
|
|
191
|
-
box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);border-color:#2563eb;}
|
|
192
|
-
input::-moz-placeholder,textarea::-moz-placeholder{color:#6b7280;opacity:1}
|
|
193
|
-
input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#6b7280;opacity:1}
|
|
194
|
-
input::placeholder,textarea::placeholder{color:#6b7280;opacity:1}
|
|
195
|
-
select{
|
|
196
|
-
background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
|
|
197
|
-
background-position:right 0.5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;-webkit-print-color-adjust:exact;color-adjust:exact}
|
|
198
|
-
[multiple]{
|
|
199
|
-
background-image:initial;background-position:initial;background-repeat:unset;background-size:initial;padding-right:0.75rem;-webkit-print-color-adjust:unset;color-adjust:unset;}
|
|
200
|
-
[type='radio']{
|
|
201
|
-
-webkit-appearance:none;-moz-appearance:none;appearance:none;padding:0;-webkit-print-color-adjust:exact;color-adjust:exact;display:inline-block;
|
|
202
|
-
vertical-align:middle;background-origin:border-box;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;
|
|
203
|
-
flex-shrink:0;height:1rem;width:1rem;color:#2563eb;background-color:#fff;border-width:1px}
|
|
204
|
-
[type='radio']{border-radius:100%}
|
|
205
|
-
[type='radio']:focus{
|
|
206
|
-
outline:2px solid transparent;outline-offset:2px;
|
|
207
|
-
--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:2px;--tw-ring-offset-color:#fff;--tw-ring-color:#2563eb;
|
|
208
|
-
--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
|
|
209
|
-
--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
|
|
210
|
-
box-shadow:var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000)}
|
|
211
|
-
[type='radio']:checked{
|
|
212
|
-
border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:center;background-repeat:no-repeat}
|
|
213
|
-
[type='radio']:checked{
|
|
214
|
-
background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e")}
|
|
215
|
-
[type='radio']:checked:hover,[type='radio']:checked:focus{
|
|
216
|
-
border-color:transparent;background-color:currentColor}
|
|
217
|
-
[type='file']{background:unset;border-color:inherit;border-width:0;border-radius:0;padding:0;font-size:unset;line-height:inherit}
|
|
218
|
-
[type='file']:focus{outline:1px auto -webkit-focus-ring-color;}
|
|
219
|
-
[type='color']{height:2.4rem;padding:2px 3px}
|
|
220
|
-
[type='range']{height:2.4rem}
|
|
221
|
-
[type='button'],button[type='submit']{cursor:pointer}
|
|
222
|
-
|
|
223
|
-
@media (min-width: 640px) {
|
|
224
|
-
[type='text'],[type='email'],[type='url'],[type='password'],[type='number'],[type='date'],[type='datetime-local'],[type='month'],[type='week'],[type='search'],[type='tel'],[type='time'],[type='color'],[multiple],textarea,select {
|
|
225
|
-
font-size: .875rem;
|
|
226
|
-
line-height: 1.25rem;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/* dark mode autocomplete fields */
|
|
231
|
-
.dark input:-webkit-autofill,
|
|
232
|
-
.dark input:-webkit-autofill:hover,
|
|
233
|
-
.dark input:-webkit-autofill:focus,
|
|
234
|
-
.dark input:-webkit-autofill:active {
|
|
235
|
-
transition: background-color 5000s ease-in-out 0s;
|
|
236
|
-
-webkit-text-fill-color: #ffffff;
|
|
237
|
-
}
|
|
238
|
-
.dark input[data-autocompleted] {
|
|
239
|
-
background-color: transparent !important;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
/* @tailwindcss/aspect css */
|
|
243
|
-
.aspect-h-9 {
|
|
244
|
-
--tw-aspect-h: 9;
|
|
245
|
-
}
|
|
246
|
-
.aspect-w-16 {
|
|
247
|
-
position: relative;
|
|
248
|
-
padding-bottom: calc(var(--tw-aspect-h) / var(--tw-aspect-w) * 100%);
|
|
249
|
-
--tw-aspect-w: 16;
|
|
250
|
-
}
|
|
251
|
-
.aspect-w-16 > * {
|
|
252
|
-
position: absolute;
|
|
253
|
-
height: 100%;
|
|
254
|
-
width: 100%;
|
|
255
|
-
top: 0;
|
|
256
|
-
right: 0;
|
|
257
|
-
bottom: 0;
|
|
258
|
-
left: 0;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
}
|
|
@@ -1,273 +0,0 @@
|
|
|
1
|
-
import { ref, computed } from 'vue'
|
|
2
|
-
import { openDB } from './lib/idb.min.mjs'
|
|
3
|
-
import { nextId } from './utils.mjs'
|
|
4
|
-
|
|
5
|
-
// Thread store for managing chat threads with IndexedDB
|
|
6
|
-
const threads = ref([])
|
|
7
|
-
const currentThread = ref(null)
|
|
8
|
-
const isLoading = ref(false)
|
|
9
|
-
|
|
10
|
-
let db = null
|
|
11
|
-
|
|
12
|
-
// Initialize IndexedDB
|
|
13
|
-
async function initDB() {
|
|
14
|
-
if (db) return db
|
|
15
|
-
|
|
16
|
-
db = await openDB('LlmsThreads', 1, {
|
|
17
|
-
upgrade(db) {
|
|
18
|
-
// Create threads store
|
|
19
|
-
const threadStore = db.createObjectStore('threads', {
|
|
20
|
-
keyPath: 'id',
|
|
21
|
-
autoIncrement: false
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
// Create indexes for efficient querying
|
|
25
|
-
threadStore.createIndex('createdAt', 'createdAt')
|
|
26
|
-
threadStore.createIndex('updatedAt', 'updatedAt')
|
|
27
|
-
threadStore.createIndex('title', 'title')
|
|
28
|
-
}
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
return db
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// Generate unique thread ID
|
|
35
|
-
function generateThreadId() {
|
|
36
|
-
return Date.now().toString()
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Create a new thread
|
|
40
|
-
async function createThread(title = 'New Chat', model = '', systemPrompt = '') {
|
|
41
|
-
await initDB()
|
|
42
|
-
|
|
43
|
-
const thread = {
|
|
44
|
-
id: generateThreadId(),
|
|
45
|
-
title: title,
|
|
46
|
-
model: model,
|
|
47
|
-
systemPrompt: systemPrompt,
|
|
48
|
-
messages: [],
|
|
49
|
-
createdAt: new Date().toISOString(),
|
|
50
|
-
updatedAt: new Date().toISOString()
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const tx = db.transaction(['threads'], 'readwrite')
|
|
54
|
-
await tx.objectStore('threads').add(thread)
|
|
55
|
-
await tx.complete
|
|
56
|
-
|
|
57
|
-
threads.value.unshift(thread)
|
|
58
|
-
// Note: currentThread will be set by router navigation
|
|
59
|
-
|
|
60
|
-
return thread
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Update thread
|
|
64
|
-
async function updateThread(threadId, updates) {
|
|
65
|
-
await initDB()
|
|
66
|
-
|
|
67
|
-
const tx = db.transaction(['threads'], 'readwrite')
|
|
68
|
-
const store = tx.objectStore('threads')
|
|
69
|
-
|
|
70
|
-
const thread = await store.get(threadId)
|
|
71
|
-
if (!thread) throw new Error('Thread not found')
|
|
72
|
-
|
|
73
|
-
const updatedThread = {
|
|
74
|
-
...thread,
|
|
75
|
-
...updates,
|
|
76
|
-
updatedAt: new Date().toISOString()
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
await store.put(updatedThread)
|
|
80
|
-
await tx.complete
|
|
81
|
-
|
|
82
|
-
// Update in memory
|
|
83
|
-
const index = threads.value.findIndex(t => t.id === threadId)
|
|
84
|
-
if (index !== -1) {
|
|
85
|
-
threads.value[index] = updatedThread
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (currentThread.value?.id === threadId) {
|
|
89
|
-
currentThread.value = updatedThread
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return updatedThread
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Add message to thread
|
|
96
|
-
async function addMessageToThread(threadId, message) {
|
|
97
|
-
const thread = await getThread(threadId)
|
|
98
|
-
if (!thread) throw new Error('Thread not found')
|
|
99
|
-
|
|
100
|
-
const newMessage = {
|
|
101
|
-
id: nextId(),
|
|
102
|
-
timestamp: new Date().toISOString(),
|
|
103
|
-
...message
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const updatedMessages = [...thread.messages, newMessage]
|
|
107
|
-
|
|
108
|
-
// Auto-generate title from first user message if still "New Chat"
|
|
109
|
-
let title = thread.title
|
|
110
|
-
if (title === 'New Chat' && message.role === 'user' && updatedMessages.length <= 2) {
|
|
111
|
-
title = message.content.slice(0, 200) + (message.content.length > 200 ? '...' : '')
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
await updateThread(threadId, {
|
|
115
|
-
messages: updatedMessages,
|
|
116
|
-
title: title
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
return newMessage
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
async function deleteMessageFromThread(threadId, messageId) {
|
|
123
|
-
const thread = await getThread(threadId)
|
|
124
|
-
if (!thread) throw new Error('Thread not found')
|
|
125
|
-
const updatedMessages = thread.messages.filter(m => m.id !== messageId)
|
|
126
|
-
await updateThread(threadId, { messages: updatedMessages })
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Get all threads
|
|
130
|
-
async function loadThreads() {
|
|
131
|
-
await initDB()
|
|
132
|
-
isLoading.value = true
|
|
133
|
-
|
|
134
|
-
try {
|
|
135
|
-
const tx = db.transaction(['threads'], 'readonly')
|
|
136
|
-
const store = tx.objectStore('threads')
|
|
137
|
-
const index = store.index('updatedAt')
|
|
138
|
-
|
|
139
|
-
const allThreads = await index.getAll()
|
|
140
|
-
threads.value = allThreads.reverse() // Most recent first
|
|
141
|
-
|
|
142
|
-
return threads.value
|
|
143
|
-
} finally {
|
|
144
|
-
isLoading.value = false
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Get single thread
|
|
149
|
-
async function getThread(threadId) {
|
|
150
|
-
await initDB()
|
|
151
|
-
|
|
152
|
-
const tx = db.transaction(['threads'], 'readonly')
|
|
153
|
-
const thread = await tx.objectStore('threads').get(threadId)
|
|
154
|
-
|
|
155
|
-
return thread
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Delete thread
|
|
159
|
-
async function deleteThread(threadId) {
|
|
160
|
-
await initDB()
|
|
161
|
-
|
|
162
|
-
const tx = db.transaction(['threads'], 'readwrite')
|
|
163
|
-
await tx.objectStore('threads').delete(threadId)
|
|
164
|
-
await tx.complete
|
|
165
|
-
|
|
166
|
-
threads.value = threads.value.filter(t => t.id !== threadId)
|
|
167
|
-
|
|
168
|
-
if (currentThread.value?.id === threadId) {
|
|
169
|
-
currentThread.value = null
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// Set current thread
|
|
174
|
-
async function setCurrentThread(threadId) {
|
|
175
|
-
const thread = await getThread(threadId)
|
|
176
|
-
if (thread) {
|
|
177
|
-
currentThread.value = thread
|
|
178
|
-
}
|
|
179
|
-
return thread
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// Set current thread from router params (router-aware version)
|
|
183
|
-
async function setCurrentThreadFromRoute(threadId, router) {
|
|
184
|
-
if (!threadId) {
|
|
185
|
-
currentThread.value = null
|
|
186
|
-
return null
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const thread = await getThread(threadId)
|
|
190
|
-
if (thread) {
|
|
191
|
-
currentThread.value = thread
|
|
192
|
-
return thread
|
|
193
|
-
} else {
|
|
194
|
-
// Thread not found, redirect to home
|
|
195
|
-
if (router) {
|
|
196
|
-
router.push('/')
|
|
197
|
-
}
|
|
198
|
-
currentThread.value = null
|
|
199
|
-
return null
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// Clear current thread (go back to initial state)
|
|
204
|
-
function clearCurrentThread() {
|
|
205
|
-
currentThread.value = null
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
function getGroupedThreads(total) {
|
|
209
|
-
const now = new Date()
|
|
210
|
-
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate())
|
|
211
|
-
const lastWeek = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000)
|
|
212
|
-
const lastMonth = new Date(today.getTime() - 30 * 24 * 60 * 60 * 1000)
|
|
213
|
-
|
|
214
|
-
const groups = {
|
|
215
|
-
today: [],
|
|
216
|
-
lastWeek: [],
|
|
217
|
-
lastMonth: [],
|
|
218
|
-
older: {}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
const takeThreads = threads.value.slice(0, total)
|
|
222
|
-
|
|
223
|
-
takeThreads.forEach(thread => {
|
|
224
|
-
const threadDate = new Date(thread.updatedAt)
|
|
225
|
-
|
|
226
|
-
if (threadDate >= today) {
|
|
227
|
-
groups.today.push(thread)
|
|
228
|
-
} else if (threadDate >= lastWeek) {
|
|
229
|
-
groups.lastWeek.push(thread)
|
|
230
|
-
} else if (threadDate >= lastMonth) {
|
|
231
|
-
groups.lastMonth.push(thread)
|
|
232
|
-
} else {
|
|
233
|
-
const year = threadDate.getFullYear()
|
|
234
|
-
const month = threadDate.toLocaleString('default', { month: 'long' })
|
|
235
|
-
const key = `${month} ${year}`
|
|
236
|
-
|
|
237
|
-
if (!groups.older[key]) {
|
|
238
|
-
groups.older[key] = []
|
|
239
|
-
}
|
|
240
|
-
groups.older[key].push(thread)
|
|
241
|
-
}
|
|
242
|
-
})
|
|
243
|
-
|
|
244
|
-
return groups
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
// Group threads by time periods
|
|
248
|
-
const groupedThreads = computed(() => getGroupedThreads(threads.value.length))
|
|
249
|
-
|
|
250
|
-
// Export the store
|
|
251
|
-
export function useThreadStore() {
|
|
252
|
-
return {
|
|
253
|
-
// State
|
|
254
|
-
threads,
|
|
255
|
-
currentThread,
|
|
256
|
-
isLoading,
|
|
257
|
-
groupedThreads,
|
|
258
|
-
|
|
259
|
-
// Actions
|
|
260
|
-
initDB,
|
|
261
|
-
createThread,
|
|
262
|
-
updateThread,
|
|
263
|
-
addMessageToThread,
|
|
264
|
-
deleteMessageFromThread,
|
|
265
|
-
loadThreads,
|
|
266
|
-
getThread,
|
|
267
|
-
deleteThread,
|
|
268
|
-
setCurrentThread,
|
|
269
|
-
setCurrentThreadFromRoute,
|
|
270
|
-
clearCurrentThread,
|
|
271
|
-
getGroupedThreads,
|
|
272
|
-
}
|
|
273
|
-
}
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
import { $$, createElement, rightPart } from "@servicestack/client"
|
|
2
|
-
|
|
3
|
-
export function toJsonArray(json) {
|
|
4
|
-
try {
|
|
5
|
-
return json ? JSON.parse(json) : []
|
|
6
|
-
} catch (e) {
|
|
7
|
-
return []
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function toJsonObject(json) {
|
|
12
|
-
try {
|
|
13
|
-
return json ? JSON.parse(json) : null
|
|
14
|
-
} catch (e) {
|
|
15
|
-
return null
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function storageArray(key) {
|
|
20
|
-
return toJsonArray(localStorage.getItem(key)) ?? []
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function storageObject(key) {
|
|
24
|
-
return toJsonObject(localStorage.getItem(key)) ?? {}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function deepClone(obj) {
|
|
28
|
-
return JSON.parse(JSON.stringify(obj))
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function fileToBase64(file) {
|
|
32
|
-
return new Promise((resolve, reject) => {
|
|
33
|
-
const reader = new FileReader()
|
|
34
|
-
reader.readAsDataURL(file) //= "data:…;base64,…"
|
|
35
|
-
reader.onload = () => {
|
|
36
|
-
resolve(rightPart(reader.result, ',')) // strip prefix
|
|
37
|
-
}
|
|
38
|
-
reader.onerror = err => reject(err)
|
|
39
|
-
})
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function fileToDataUri(file) {
|
|
43
|
-
return new Promise((resolve, reject) => {
|
|
44
|
-
const reader = new FileReader()
|
|
45
|
-
reader.readAsDataURL(file) //= "data:…;base64,…"
|
|
46
|
-
reader.onload = () => resolve(reader.result)
|
|
47
|
-
reader.onerror = err => reject(err)
|
|
48
|
-
})
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const svg = {
|
|
52
|
-
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>`,
|
|
53
|
-
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>`,
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function copyBlock(btn) {
|
|
57
|
-
// console.log('copyBlock',btn)
|
|
58
|
-
const label = btn.previousElementSibling
|
|
59
|
-
const code = btn.parentElement.nextElementSibling
|
|
60
|
-
label.classList.remove('hidden')
|
|
61
|
-
label.innerHTML = 'copied'
|
|
62
|
-
btn.classList.add('border-gray-600', 'bg-gray-700')
|
|
63
|
-
btn.classList.remove('border-gray-700')
|
|
64
|
-
btn.innerHTML = svg.check
|
|
65
|
-
navigator.clipboard.writeText(code.innerText)
|
|
66
|
-
setTimeout(() => {
|
|
67
|
-
label.classList.add('hidden')
|
|
68
|
-
label.innerHTML = ''
|
|
69
|
-
btn.innerHTML = svg.clipboard
|
|
70
|
-
btn.classList.remove('border-gray-600', 'bg-gray-700')
|
|
71
|
-
btn.classList.add('border-gray-700')
|
|
72
|
-
}, 2000)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export function addCopyButtonToCodeBlocks(sel) {
|
|
76
|
-
globalThis.copyBlock ??= copyBlock
|
|
77
|
-
//console.log('addCopyButtonToCodeBlocks', sel, [...$$(sel)].length)
|
|
78
|
-
|
|
79
|
-
$$(sel).forEach(code => {
|
|
80
|
-
let pre = code.parentElement;
|
|
81
|
-
if (pre.classList.contains('group')) return
|
|
82
|
-
pre.classList.add('relative', 'group')
|
|
83
|
-
|
|
84
|
-
const div = createElement('div', {attrs: {className: 'opacity-0 group-hover:opacity-100 transition-opacity duration-100 flex absolute right-2 -mt-1 select-none'}})
|
|
85
|
-
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'}})
|
|
86
|
-
const btn = createElement('button', {
|
|
87
|
-
attrs: {
|
|
88
|
-
type: 'button',
|
|
89
|
-
className: 'p-1 rounded-md border block text-gray-500 hover:text-gray-400 border-gray-700 hover:border-gray-600',
|
|
90
|
-
onclick: 'copyBlock(this)'
|
|
91
|
-
}
|
|
92
|
-
})
|
|
93
|
-
btn.innerHTML = svg.clipboard
|
|
94
|
-
div.appendChild(label)
|
|
95
|
-
div.appendChild(btn)
|
|
96
|
-
pre.insertBefore(div, code)
|
|
97
|
-
})
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export function addCopyButtons() {
|
|
101
|
-
addCopyButtonToCodeBlocks('.prose pre>code')
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Returns an ever-increasing unique integer id.
|
|
106
|
-
*/
|
|
107
|
-
export const nextId = (() => {
|
|
108
|
-
let last = 0 // cache of the last id that was handed out
|
|
109
|
-
return () => {
|
|
110
|
-
const now = Date.now() // current millisecond timestamp
|
|
111
|
-
last = (now > last) ? now : last + 1
|
|
112
|
-
return last
|
|
113
|
-
}
|
|
114
|
-
})();
|