llms-py 2.0.20__py3-none-any.whl → 3.0.18__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 +3 -1
- llms/db.py +359 -0
- llms/{ui/Analytics.mjs → extensions/analytics/ui/index.mjs} +254 -327
- llms/extensions/app/README.md +20 -0
- llms/extensions/app/__init__.py +588 -0
- llms/extensions/app/db.py +540 -0
- llms/{ui → extensions/app/ui}/Recents.mjs +99 -73
- llms/{ui/Sidebar.mjs → extensions/app/ui/index.mjs} +139 -68
- llms/extensions/app/ui/threadStore.mjs +440 -0
- llms/extensions/computer/README.md +96 -0
- llms/extensions/computer/__init__.py +59 -0
- llms/extensions/computer/base.py +80 -0
- llms/extensions/computer/bash.py +185 -0
- llms/extensions/computer/computer.py +523 -0
- llms/extensions/computer/edit.py +299 -0
- llms/extensions/computer/filesystem.py +542 -0
- llms/extensions/computer/platform.py +461 -0
- llms/extensions/computer/run.py +37 -0
- llms/extensions/core_tools/CALCULATOR.md +32 -0
- llms/extensions/core_tools/__init__.py +599 -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 +260 -0
- llms/extensions/providers/cerebras.py +36 -0
- llms/extensions/providers/chutes.py +153 -0
- llms/extensions/providers/google.py +559 -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/skills/LICENSE +202 -0
- llms/extensions/skills/__init__.py +130 -0
- llms/extensions/skills/errors.py +25 -0
- llms/extensions/skills/models.py +39 -0
- llms/extensions/skills/parser.py +178 -0
- llms/extensions/skills/ui/index.mjs +376 -0
- llms/extensions/skills/ui/skills/create-plan/SKILL.md +74 -0
- llms/extensions/skills/validator.py +177 -0
- llms/extensions/system_prompts/README.md +22 -0
- llms/extensions/system_prompts/__init__.py +45 -0
- llms/extensions/system_prompts/ui/index.mjs +276 -0
- llms/extensions/system_prompts/ui/prompts.json +1067 -0
- llms/extensions/tools/__init__.py +67 -0
- llms/extensions/tools/ui/index.mjs +837 -0
- llms/index.html +36 -62
- llms/llms.json +180 -879
- llms/main.py +4009 -912
- llms/providers-extra.json +394 -0
- llms/providers.json +1 -0
- llms/ui/App.mjs +176 -8
- llms/ui/ai.mjs +156 -20
- llms/ui/app.css +3768 -321
- llms/ui/ctx.mjs +459 -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/highlight.min.mjs +1243 -0
- llms/ui/lib/idb.min.mjs +8 -0
- llms/ui/lib/marked.min.mjs +8 -0
- llms/ui/lib/servicestack-client.mjs +1 -0
- llms/ui/lib/servicestack-vue.mjs +37 -0
- llms/ui/lib/vue-router.min.mjs +6 -0
- llms/ui/lib/vue.min.mjs +13 -0
- llms/ui/lib/vue.mjs +18530 -0
- llms/ui/markdown.mjs +25 -14
- llms/ui/modules/chat/ChatBody.mjs +1156 -0
- llms/ui/{SettingsDialog.mjs → modules/chat/SettingsDialog.mjs} +74 -74
- llms/ui/modules/chat/index.mjs +995 -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 +560 -78
- llms/ui/typography.css +54 -36
- llms/ui/utils.mjs +221 -92
- llms_py-3.0.18.dist-info/METADATA +49 -0
- llms_py-3.0.18.dist-info/RECORD +194 -0
- {llms_py-2.0.20.dist-info → llms_py-3.0.18.dist-info}/WHEEL +1 -1
- {llms_py-2.0.20.dist-info → llms_py-3.0.18.dist-info}/licenses/LICENSE +1 -2
- llms/ui/Avatar.mjs +0 -28
- llms/ui/Brand.mjs +0 -34
- llms/ui/ChatPrompt.mjs +0 -443
- llms/ui/Main.mjs +0 -740
- llms/ui/ModelSelector.mjs +0 -60
- llms/ui/ProviderIcon.mjs +0 -29
- llms/ui/ProviderStatus.mjs +0 -105
- llms/ui/SignIn.mjs +0 -64
- llms/ui/SystemPromptEditor.mjs +0 -31
- llms/ui/SystemPromptSelector.mjs +0 -36
- llms/ui/Welcome.mjs +0 -8
- llms/ui/threadStore.mjs +0 -524
- llms/ui.json +0 -1069
- llms_py-2.0.20.dist-info/METADATA +0 -931
- llms_py-2.0.20.dist-info/RECORD +0 -36
- {llms_py-2.0.20.dist-info → llms_py-3.0.18.dist-info}/entry_points.txt +0 -0
- {llms_py-2.0.20.dist-info → llms_py-3.0.18.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,650 @@
|
|
|
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
|
+
}
|