@anvaka/vue-llm 0.1.0
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.
- package/README.md +166 -0
- package/dist/_virtual/_plugin-vue_export-helper.js +9 -0
- package/dist/core/LLMClient.js +248 -0
- package/dist/core/configStore.js +52 -0
- package/dist/core/keyStore.js +58 -0
- package/dist/core/storageAdapter.js +67 -0
- package/dist/index.js +38 -0
- package/dist/providers/AnthropicProvider.js +88 -0
- package/dist/providers/BaseProvider.js +155 -0
- package/dist/providers/CustomProvider.js +53 -0
- package/dist/providers/GeminiProvider.js +163 -0
- package/dist/providers/GrokProvider.js +70 -0
- package/dist/providers/LlamaServerProvider.js +56 -0
- package/dist/providers/OllamaProvider.js +77 -0
- package/dist/providers/OpenAIProvider.js +98 -0
- package/dist/providers/OpenRouterProvider.js +81 -0
- package/dist/providers/factory.js +69 -0
- package/dist/providers/index.js +12 -0
- package/dist/vue/components/LLMConfigModal.vue.js +498 -0
- package/dist/vue/components/ProviderSelector.vue.js +53 -0
- package/dist/vue/components/StoredKeysManager.vue.js +223 -0
- package/dist/vue/index.js +17 -0
- package/dist/vue/plugin.js +26 -0
- package/dist/vue/useLLM.js +137 -0
- package/dist/vue-llm.css +1 -0
- package/package.json +41 -0
- package/src/styles/components.css +15 -0
- package/src/styles/variables.css +70 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { ref as v, watch as R, onMounted as G, createElementBlock as d, openBlock as c, createElementVNode as s, createCommentVNode as b, Fragment as D, renderList as V, toDisplayString as p, withDirectives as S, withKeys as K, vModelDynamic as U, unref as q, vModelSelect as z, nextTick as J } from "vue";
|
|
2
|
+
import { useLLM as Q } from "../useLLM.js";
|
|
3
|
+
import { DEFAULT_CONFIGS as W } from "../../providers/factory.js";
|
|
4
|
+
import { maskApiKey as X } from "../../core/keyStore.js";
|
|
5
|
+
/* empty css */
|
|
6
|
+
import Y from "../../_virtual/_plugin-vue_export-helper.js";
|
|
7
|
+
const Z = { class: "llm-stored-keys-manager" }, I = { class: "llm-manager-header" }, ee = { class: "llm-manager-body" }, te = { class: "llm-scrollable-content" }, le = {
|
|
8
|
+
key: 0,
|
|
9
|
+
class: "llm-empty-keys"
|
|
10
|
+
}, se = {
|
|
11
|
+
key: 1,
|
|
12
|
+
class: "llm-keys-list"
|
|
13
|
+
}, oe = { class: "llm-key-header" }, ne = { class: "llm-provider-info" }, ie = { class: "llm-provider-header" }, re = { class: "llm-provider-name" }, ae = { class: "llm-stored-date" }, de = {
|
|
14
|
+
key: 0,
|
|
15
|
+
class: "llm-service-endpoint"
|
|
16
|
+
}, ce = { class: "llm-key-input-row" }, ue = ["onUpdate:modelValue", "type", "placeholder", "onKeyup", "onBlur"], me = ["onClick", "title"], ve = ["onClick"], pe = {
|
|
17
|
+
key: 0,
|
|
18
|
+
class: "llm-key-item"
|
|
19
|
+
}, ye = { class: "llm-key-header" }, fe = { class: "llm-provider-info" }, _e = { class: "llm-provider-header" }, he = ["value", "disabled"], be = { class: "llm-key-input-row" }, Ke = ["type"], we = ["title"], ge = { class: "llm-manager-footer" }, Se = {
|
|
20
|
+
__name: "StoredKeysManager",
|
|
21
|
+
emits: ["close", "keysUpdated"],
|
|
22
|
+
setup(Ne, { emit: P }) {
|
|
23
|
+
const w = P, _ = W, {
|
|
24
|
+
getAllStoredKeys: M,
|
|
25
|
+
getStoredKey: N,
|
|
26
|
+
storeKey: C,
|
|
27
|
+
deleteStoredKey: F,
|
|
28
|
+
hasStoredKey: L
|
|
29
|
+
} = Q(), y = v({}), r = v({}), a = v({}), f = v(!1), i = v(""), u = v(""), m = v(!1), k = (e) => L(e), h = () => {
|
|
30
|
+
const e = M(), t = {};
|
|
31
|
+
for (const [o, l] of Object.entries(e)) {
|
|
32
|
+
let n = o, g = l.providerType || o;
|
|
33
|
+
if (l.providerType === "custom")
|
|
34
|
+
if (l.providerName)
|
|
35
|
+
n = l.providerName;
|
|
36
|
+
else if (l.serviceEndpoint)
|
|
37
|
+
try {
|
|
38
|
+
n = `Custom (${new URL(l.serviceEndpoint).hostname})`;
|
|
39
|
+
} catch {
|
|
40
|
+
n = "Custom Provider";
|
|
41
|
+
}
|
|
42
|
+
else
|
|
43
|
+
n = "Custom Provider";
|
|
44
|
+
else _[g] && (n = _[g].name);
|
|
45
|
+
t[o] = {
|
|
46
|
+
maskedKey: X(l.apiKey),
|
|
47
|
+
storedAt: l.storedAt,
|
|
48
|
+
providerType: l.providerType || o,
|
|
49
|
+
serviceEndpoint: l.serviceEndpoint,
|
|
50
|
+
providerName: l.providerName,
|
|
51
|
+
displayName: n
|
|
52
|
+
}, r.value[o] || (r.value[o] = ""), a.value[o] === void 0 && (a.value[o] = !1);
|
|
53
|
+
}
|
|
54
|
+
y.value = t;
|
|
55
|
+
}, E = (e) => {
|
|
56
|
+
var o;
|
|
57
|
+
const t = y.value[e];
|
|
58
|
+
return (t == null ? void 0 : t.displayName) || ((o = _[e]) == null ? void 0 : o.name) || e;
|
|
59
|
+
}, O = (e) => new Date(e).toLocaleDateString(), x = (e) => {
|
|
60
|
+
a.value[e] = !a.value[e], a.value[e] && !r.value[e] && (r.value[e] = N(e) || "");
|
|
61
|
+
}, A = (e) => {
|
|
62
|
+
const t = r.value[e];
|
|
63
|
+
if (!t || !t.trim()) {
|
|
64
|
+
r.value[e] = "";
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const o = N(e);
|
|
68
|
+
if (t !== o) {
|
|
69
|
+
const l = y.value[e], n = {
|
|
70
|
+
providerType: l.providerType,
|
|
71
|
+
...l.serviceEndpoint && { serviceEndpoint: l.serviceEndpoint },
|
|
72
|
+
...l.providerName && { providerName: l.providerName }
|
|
73
|
+
};
|
|
74
|
+
C(e, t, n) && (h(), w("keysUpdated"));
|
|
75
|
+
}
|
|
76
|
+
}, B = (e) => {
|
|
77
|
+
r.value[e] = "", a.value[e] = !1;
|
|
78
|
+
}, H = (e) => {
|
|
79
|
+
const t = E(e);
|
|
80
|
+
confirm(`Delete stored API key for ${t}?
|
|
81
|
+
|
|
82
|
+
This action cannot be undone.`) && F(e) && (h(), w("keysUpdated"));
|
|
83
|
+
}, $ = () => {
|
|
84
|
+
if (!i.value || !u.value.trim()) return;
|
|
85
|
+
const e = i.value === "custom" ? `custom_${Date.now()}_${Math.random().toString(36).substr(2, 9)}` : i.value, t = { providerType: i.value };
|
|
86
|
+
C(e, u.value, t) && (h(), w("keysUpdated"), i.value = "", u.value = "", m.value = !1, f.value = !1);
|
|
87
|
+
}, T = () => {
|
|
88
|
+
i.value = "", u.value = "", m.value = !1, f.value = !1;
|
|
89
|
+
}, j = () => {
|
|
90
|
+
f.value = !0, J(() => {
|
|
91
|
+
const e = document.querySelector(".llm-key-item:last-child");
|
|
92
|
+
e && e.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
|
93
|
+
});
|
|
94
|
+
};
|
|
95
|
+
return R([i, u], ([e, t]) => {
|
|
96
|
+
e && t && t.trim() && setTimeout(() => {
|
|
97
|
+
i.value === e && u.value === t && $();
|
|
98
|
+
}, 500);
|
|
99
|
+
}), G(() => {
|
|
100
|
+
h();
|
|
101
|
+
}), (e, t) => (c(), d("div", Z, [
|
|
102
|
+
s("div", I, [
|
|
103
|
+
t[5] || (t[5] = s("h3", null, "Stored API Keys", -1)),
|
|
104
|
+
s("button", {
|
|
105
|
+
class: "llm-btn llm-btn--ghost llm-btn--sm llm-close-btn",
|
|
106
|
+
onClick: t[0] || (t[0] = (o) => e.$emit("close"))
|
|
107
|
+
}, "×")
|
|
108
|
+
]),
|
|
109
|
+
s("div", ee, [
|
|
110
|
+
s("div", te, [
|
|
111
|
+
Object.keys(y.value).length === 0 && !f.value ? (c(), d("div", le, [...t[6] || (t[6] = [
|
|
112
|
+
s("p", null, "No API keys stored yet.", -1),
|
|
113
|
+
s("small", null, "Keys are automatically stored when you save a provider configuration.", -1)
|
|
114
|
+
])])) : b("", !0),
|
|
115
|
+
Object.keys(y.value).length > 0 || f.value ? (c(), d("div", se, [
|
|
116
|
+
(c(!0), d(D, null, V(y.value, (o, l) => (c(), d("div", {
|
|
117
|
+
key: l,
|
|
118
|
+
class: "llm-key-item"
|
|
119
|
+
}, [
|
|
120
|
+
s("div", oe, [
|
|
121
|
+
s("div", ne, [
|
|
122
|
+
s("div", ie, [
|
|
123
|
+
s("div", re, p(E(l)), 1),
|
|
124
|
+
s("span", ae, p(O(o.storedAt)), 1)
|
|
125
|
+
]),
|
|
126
|
+
o.serviceEndpoint ? (c(), d("div", de, [
|
|
127
|
+
s("small", null, p(o.serviceEndpoint), 1)
|
|
128
|
+
])) : b("", !0),
|
|
129
|
+
s("div", ce, [
|
|
130
|
+
S(s("input", {
|
|
131
|
+
"onUpdate:modelValue": (n) => r.value[l] = n,
|
|
132
|
+
type: a.value[l] ? "text" : "password",
|
|
133
|
+
class: "llm-form-control llm-key-input-inline",
|
|
134
|
+
placeholder: o.maskedKey,
|
|
135
|
+
onKeyup: [
|
|
136
|
+
K((n) => A(l), ["enter"]),
|
|
137
|
+
K((n) => B(l), ["escape"])
|
|
138
|
+
],
|
|
139
|
+
onBlur: (n) => A(l)
|
|
140
|
+
}, null, 40, ue), [
|
|
141
|
+
[U, r.value[l]]
|
|
142
|
+
]),
|
|
143
|
+
s("button", {
|
|
144
|
+
type: "button",
|
|
145
|
+
class: "llm-btn llm-btn--ghost llm-btn--sm",
|
|
146
|
+
onClick: (n) => x(l),
|
|
147
|
+
title: a.value[l] ? "Hide key" : "Show key"
|
|
148
|
+
}, p(a.value[l] ? "Hide" : "Show"), 9, me),
|
|
149
|
+
s("button", {
|
|
150
|
+
class: "llm-btn llm-btn--sm llm-btn--danger",
|
|
151
|
+
onClick: (n) => H(l),
|
|
152
|
+
title: "Delete key"
|
|
153
|
+
}, " Delete ", 8, ve)
|
|
154
|
+
])
|
|
155
|
+
])
|
|
156
|
+
])
|
|
157
|
+
]))), 128)),
|
|
158
|
+
f.value ? (c(), d("div", pe, [
|
|
159
|
+
s("div", ye, [
|
|
160
|
+
s("div", fe, [
|
|
161
|
+
s("div", _e, [
|
|
162
|
+
S(s("select", {
|
|
163
|
+
"onUpdate:modelValue": t[1] || (t[1] = (o) => i.value = o),
|
|
164
|
+
class: "llm-form-control llm-provider-select"
|
|
165
|
+
}, [
|
|
166
|
+
t[7] || (t[7] = s("option", { value: "" }, "Select provider type...", -1)),
|
|
167
|
+
(c(!0), d(D, null, V(q(_), (o, l) => (c(), d("option", {
|
|
168
|
+
key: l,
|
|
169
|
+
value: l,
|
|
170
|
+
disabled: l !== "custom" && k(l)
|
|
171
|
+
}, p(o.name) + " " + p(l !== "custom" && k(l) ? "(already stored)" : ""), 9, he))), 128))
|
|
172
|
+
], 512), [
|
|
173
|
+
[z, i.value]
|
|
174
|
+
])
|
|
175
|
+
]),
|
|
176
|
+
s("div", be, [
|
|
177
|
+
S(s("input", {
|
|
178
|
+
"onUpdate:modelValue": t[2] || (t[2] = (o) => u.value = o),
|
|
179
|
+
type: m.value ? "text" : "password",
|
|
180
|
+
class: "llm-form-control llm-key-input-inline",
|
|
181
|
+
placeholder: "Enter API key",
|
|
182
|
+
onKeyup: [
|
|
183
|
+
K($, ["enter"]),
|
|
184
|
+
K(T, ["escape"])
|
|
185
|
+
]
|
|
186
|
+
}, null, 40, Ke), [
|
|
187
|
+
[U, u.value]
|
|
188
|
+
]),
|
|
189
|
+
s("button", {
|
|
190
|
+
type: "button",
|
|
191
|
+
class: "llm-btn llm-btn--ghost llm-btn--sm",
|
|
192
|
+
onClick: t[3] || (t[3] = (o) => m.value = !m.value),
|
|
193
|
+
title: m.value ? "Hide key" : "Show key"
|
|
194
|
+
}, p(m.value ? "Hide" : "Show"), 9, we),
|
|
195
|
+
s("button", {
|
|
196
|
+
class: "llm-btn llm-btn--sm llm-btn--danger",
|
|
197
|
+
onClick: T,
|
|
198
|
+
title: "Cancel"
|
|
199
|
+
}, " Cancel ")
|
|
200
|
+
])
|
|
201
|
+
])
|
|
202
|
+
])
|
|
203
|
+
])) : b("", !0)
|
|
204
|
+
])) : b("", !0)
|
|
205
|
+
])
|
|
206
|
+
]),
|
|
207
|
+
s("div", ge, [
|
|
208
|
+
s("button", {
|
|
209
|
+
class: "llm-btn llm-btn--secondary",
|
|
210
|
+
onClick: j
|
|
211
|
+
}, " Add New Key "),
|
|
212
|
+
t[8] || (t[8] = s("div", { class: "llm-footer-spacer" }, null, -1)),
|
|
213
|
+
s("button", {
|
|
214
|
+
class: "llm-btn llm-btn--ghost",
|
|
215
|
+
onClick: t[4] || (t[4] = (o) => e.$emit("close"))
|
|
216
|
+
}, " Close ")
|
|
217
|
+
])
|
|
218
|
+
]));
|
|
219
|
+
}
|
|
220
|
+
}, De = /* @__PURE__ */ Y(Se, [["__scopeId", "data-v-dd3230e5"]]);
|
|
221
|
+
export {
|
|
222
|
+
De as default
|
|
223
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { LLMPlugin as r, LLM_CLIENT_SYMBOL as o, LLM_CONFIG_SYMBOL as t, LLM_KEYSTORE_SYMBOL as a, createLLM as M } from "./plugin.js";
|
|
2
|
+
import { createDefaultConfig as l, useLLM as d } from "./useLLM.js";
|
|
3
|
+
import { default as S } from "./components/ProviderSelector.vue.js";
|
|
4
|
+
import { default as m } from "./components/LLMConfigModal.vue.js";
|
|
5
|
+
import { default as s } from "./components/StoredKeysManager.vue.js";
|
|
6
|
+
export {
|
|
7
|
+
m as LLMConfigModal,
|
|
8
|
+
r as LLMPlugin,
|
|
9
|
+
o as LLM_CLIENT_SYMBOL,
|
|
10
|
+
t as LLM_CONFIG_SYMBOL,
|
|
11
|
+
a as LLM_KEYSTORE_SYMBOL,
|
|
12
|
+
S as ProviderSelector,
|
|
13
|
+
s as StoredKeysManager,
|
|
14
|
+
l as createDefaultConfig,
|
|
15
|
+
M as createLLM,
|
|
16
|
+
d as useLLM
|
|
17
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { LLMClient as L } from "../core/LLMClient.js";
|
|
2
|
+
import { ConfigStore as c } from "../core/configStore.js";
|
|
3
|
+
import { KeyStore as i } from "../core/keyStore.js";
|
|
4
|
+
import { LocalStorageAdapter as l } from "../core/storageAdapter.js";
|
|
5
|
+
const S = Symbol("LLM_CLIENT"), a = Symbol("LLM_CONFIG"), m = Symbol("LLM_KEYSTORE");
|
|
6
|
+
function s(e = {}) {
|
|
7
|
+
const o = e.namespace || "llm", t = e.storageAdapter || new l(o), r = new c({ storageAdapter: t }), n = new i(t);
|
|
8
|
+
return { client: new L({ configStore: r, logger: e.logger }), configStore: r, keyStore: n };
|
|
9
|
+
}
|
|
10
|
+
const y = {
|
|
11
|
+
install(e, o = {}) {
|
|
12
|
+
const { client: t, configStore: r, keyStore: n } = s(o);
|
|
13
|
+
if (e.provide(S, t), e.provide(a, r), e.provide(m, n), o.autoInit)
|
|
14
|
+
try {
|
|
15
|
+
t.ensureInitialized();
|
|
16
|
+
} catch {
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
export {
|
|
21
|
+
y as LLMPlugin,
|
|
22
|
+
S as LLM_CLIENT_SYMBOL,
|
|
23
|
+
a as LLM_CONFIG_SYMBOL,
|
|
24
|
+
m as LLM_KEYSTORE_SYMBOL,
|
|
25
|
+
s as createLLM
|
|
26
|
+
};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { inject as f, ref as c } from "vue";
|
|
2
|
+
import { LLM_CLIENT_SYMBOL as U, LLM_CONFIG_SYMBOL as Y, LLM_KEYSTORE_SYMBOL as B } from "./plugin.js";
|
|
3
|
+
import { createProvider as D } from "../providers/factory.js";
|
|
4
|
+
function S() {
|
|
5
|
+
const o = f(U), i = f(Y), n = f(B);
|
|
6
|
+
if (!o || !i)
|
|
7
|
+
throw new Error("LLM not provided. Did you install the LLMPlugin?");
|
|
8
|
+
const u = c(!1), a = c(""), l = c("");
|
|
9
|
+
async function g(e, t = {}) {
|
|
10
|
+
u.value = !0, a.value = "", l.value = "";
|
|
11
|
+
try {
|
|
12
|
+
return await o.stream({ messages: e, ...t }, (s) => {
|
|
13
|
+
a.value = s.fullContent || "", l.value = s.fullThinking || "";
|
|
14
|
+
});
|
|
15
|
+
} finally {
|
|
16
|
+
u.value = !1;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function p() {
|
|
20
|
+
return i.getEnabledConfigs();
|
|
21
|
+
}
|
|
22
|
+
function m() {
|
|
23
|
+
const e = i.listConfigs();
|
|
24
|
+
return Object.entries(e).filter(([t, r]) => r && r.provider && r.baseUrl).map(([t, r]) => ({ id: t, ...r }));
|
|
25
|
+
}
|
|
26
|
+
function C() {
|
|
27
|
+
return i.getActiveConfig();
|
|
28
|
+
}
|
|
29
|
+
function d() {
|
|
30
|
+
return i.getActiveProviderId();
|
|
31
|
+
}
|
|
32
|
+
function v(e) {
|
|
33
|
+
return i.setActiveProviderId(e), !0;
|
|
34
|
+
}
|
|
35
|
+
function L(e, t) {
|
|
36
|
+
const r = i.saveConfig(e, t);
|
|
37
|
+
if (r && t.apiKey && t.provider && n) {
|
|
38
|
+
const s = t.provider === "custom" ? e : t.provider, w = {
|
|
39
|
+
providerType: t.provider,
|
|
40
|
+
...t.provider === "custom" && t.baseUrl && { serviceEndpoint: t.baseUrl },
|
|
41
|
+
...t.provider === "custom" && t.name && { providerName: t.name }
|
|
42
|
+
};
|
|
43
|
+
n.set(s, t.apiKey, w);
|
|
44
|
+
}
|
|
45
|
+
return r;
|
|
46
|
+
}
|
|
47
|
+
function b(e) {
|
|
48
|
+
return i.deleteConfig(e);
|
|
49
|
+
}
|
|
50
|
+
function M(e) {
|
|
51
|
+
const t = i.getConfig(e);
|
|
52
|
+
return t ? (t.enabled = !0, i.saveConfig(e, t)) : !1;
|
|
53
|
+
}
|
|
54
|
+
function A(e) {
|
|
55
|
+
const t = i.getConfig(e);
|
|
56
|
+
if (!t) return !1;
|
|
57
|
+
t.enabled = !1;
|
|
58
|
+
const r = i.saveConfig(e, t);
|
|
59
|
+
return d() === e && v(null), r;
|
|
60
|
+
}
|
|
61
|
+
async function I(e, t) {
|
|
62
|
+
return D(e, t).discoverModels();
|
|
63
|
+
}
|
|
64
|
+
async function K(e) {
|
|
65
|
+
return o.testConnection(e);
|
|
66
|
+
}
|
|
67
|
+
async function h() {
|
|
68
|
+
return o.refresh();
|
|
69
|
+
}
|
|
70
|
+
function P(e) {
|
|
71
|
+
return (n == null ? void 0 : n.get(e)) || null;
|
|
72
|
+
}
|
|
73
|
+
function y(e) {
|
|
74
|
+
return (n == null ? void 0 : n.has(e)) || !1;
|
|
75
|
+
}
|
|
76
|
+
function E() {
|
|
77
|
+
return (n == null ? void 0 : n.getAll()) || {};
|
|
78
|
+
}
|
|
79
|
+
function O(e, t, r = {}) {
|
|
80
|
+
return (n == null ? void 0 : n.set(e, t, r)) || !1;
|
|
81
|
+
}
|
|
82
|
+
function T(e) {
|
|
83
|
+
return (n == null ? void 0 : n.delete(e)) || !1;
|
|
84
|
+
}
|
|
85
|
+
function _(e) {
|
|
86
|
+
return (n == null ? void 0 : n.meta(e)) || null;
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
// Core objects
|
|
90
|
+
client: o,
|
|
91
|
+
configStore: i,
|
|
92
|
+
keyStore: n,
|
|
93
|
+
// Streaming
|
|
94
|
+
stream: g,
|
|
95
|
+
isStreaming: u,
|
|
96
|
+
streamContent: a,
|
|
97
|
+
streamThinking: l,
|
|
98
|
+
// Config management
|
|
99
|
+
getEnabledConfigs: p,
|
|
100
|
+
getAllConfigs: m,
|
|
101
|
+
getActiveConfig: C,
|
|
102
|
+
getActiveProviderId: d,
|
|
103
|
+
setActiveProviderId: v,
|
|
104
|
+
saveConfig: L,
|
|
105
|
+
deleteConfig: b,
|
|
106
|
+
enableProvider: M,
|
|
107
|
+
disableProvider: A,
|
|
108
|
+
getAvailableModels: I,
|
|
109
|
+
testConnection: K,
|
|
110
|
+
refresh: h,
|
|
111
|
+
// Key management
|
|
112
|
+
getStoredKey: P,
|
|
113
|
+
hasStoredKey: y,
|
|
114
|
+
getAllStoredKeys: E,
|
|
115
|
+
storeKey: O,
|
|
116
|
+
deleteStoredKey: T,
|
|
117
|
+
getStoredKeyMeta: _
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function F(o) {
|
|
121
|
+
return {
|
|
122
|
+
name: "",
|
|
123
|
+
provider: o,
|
|
124
|
+
baseUrl: "",
|
|
125
|
+
apiKey: "",
|
|
126
|
+
model: "",
|
|
127
|
+
temperature: 0.7,
|
|
128
|
+
maxTokens: 4096,
|
|
129
|
+
enableThinking: !1,
|
|
130
|
+
enabled: !0,
|
|
131
|
+
configuredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
export {
|
|
135
|
+
F as createDefaultConfig,
|
|
136
|
+
S as useLLM
|
|
137
|
+
};
|
package/dist/vue-llm.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.llm-provider-selector[data-v-9fd186c2]{font-size:var(--llm-font-size-sm, .85rem)}.llm-provider-select[data-v-9fd186c2]{padding:var(--llm-spacing-xs, .25rem) var(--llm-spacing-sm, .5rem);border:1px solid var(--llm-border, #444);border-radius:var(--llm-radius-sm, 4px);background:var(--llm-bg, #111);color:var(--llm-text, #ddd);font-family:inherit;font-size:inherit;cursor:pointer;min-width:120px;max-width:180px}.llm-provider-select[data-v-9fd186c2]:focus{outline:none;border-color:var(--llm-accent, #6c7cff)}.llm-provider-select[data-v-9fd186c2]:hover{border-color:var(--llm-border-hover, #666)}.config-option[data-v-9fd186c2]{font-style:italic;border-top:1px solid var(--llm-border, #444)}.llm-stored-keys-manager[data-v-dd3230e5]{background:var(--llm-bg, #181a1f);border-radius:var(--llm-radius-md, 8px);max-height:80vh;display:flex;flex-direction:column;color:var(--llm-text, #e6e8ea)}.llm-manager-header[data-v-dd3230e5]{display:flex;justify-content:space-between;align-items:center;padding:1.5rem;border-bottom:1px solid var(--llm-border, #33383f);flex-shrink:0}.llm-manager-header h3[data-v-dd3230e5]{margin:0;font-size:1.25rem;font-weight:600;color:var(--llm-text, #e6e8ea)}.llm-close-btn[data-v-dd3230e5]{font-size:1.5rem;line-height:1}.llm-close-btn[data-v-dd3230e5]:hover{opacity:.7}.llm-manager-body[data-v-dd3230e5]{flex:1;overflow:hidden;min-height:0;display:flex;flex-direction:column}.llm-scrollable-content[data-v-dd3230e5]{flex:1;overflow-y:auto;padding:1.5rem;min-height:0}.llm-empty-keys[data-v-dd3230e5]{text-align:center;padding:2rem 1rem;color:var(--llm-text-dim, #9aa0a6)}.llm-empty-keys p[data-v-dd3230e5]{margin:0 0 .5rem;font-size:1rem;font-weight:500}.llm-empty-keys small[data-v-dd3230e5]{font-size:.9rem;opacity:.7}.llm-keys-list[data-v-dd3230e5]{margin-bottom:1rem}.llm-key-item[data-v-dd3230e5]{border:1px solid var(--llm-border, #33383f);border-radius:var(--llm-radius-sm, 6px);margin-bottom:.75rem;background:var(--llm-bg-soft, #1f2228);overflow:hidden}.llm-key-header[data-v-dd3230e5]{display:flex;justify-content:space-between;align-items:flex-start;padding:.75rem}.llm-provider-info[data-v-dd3230e5]{flex:1;display:flex;flex-direction:column;gap:.5rem}.llm-provider-header[data-v-dd3230e5]{display:flex;justify-content:space-between;align-items:center}.llm-provider-name[data-v-dd3230e5]{font-weight:600;font-size:.9rem;color:var(--llm-text, #e6e8ea)}.llm-service-endpoint[data-v-dd3230e5]{margin-top:.25rem}.llm-service-endpoint small[data-v-dd3230e5]{color:var(--llm-text-dim, #9aa0a6);font-family:SF Mono,Monaco,Inconsolata,Roboto Mono,monospace;font-size:.75rem}.llm-provider-select[data-v-dd3230e5]{font-weight:600;font-size:.9rem;min-width:200px}.llm-key-input-row[data-v-dd3230e5]{display:flex;gap:.5rem;align-items:center}.llm-key-input-inline[data-v-dd3230e5]{flex:1;font-family:SF Mono,Monaco,Inconsolata,Roboto Mono,monospace;font-size:.8rem;padding:.4rem .6rem}.llm-stored-date[data-v-dd3230e5]{font-size:.75rem;color:var(--llm-text-dim, #9aa0a6)}.llm-manager-footer[data-v-dd3230e5]{display:flex;align-items:center;gap:.5rem;padding:1.5rem;border-top:1px solid var(--llm-border, #33383f);flex-shrink:0}.llm-footer-spacer[data-v-dd3230e5]{flex:1}.llm-btn[data-v-dd3230e5]{padding:.5rem 1rem;border-radius:var(--llm-radius-sm, 4px);border:1px solid var(--llm-border, #33383f);background:var(--llm-bg-soft, #1f2228);color:var(--llm-text, #e6e8ea);cursor:pointer;font-size:var(--llm-font-size-sm, .85rem);transition:all .15s ease}.llm-btn[data-v-dd3230e5]:hover:not(:disabled){border-color:var(--llm-border-hover, #4b525c);background:var(--llm-bg-mute, #242830)}.llm-btn--sm[data-v-dd3230e5]{padding:.25rem .5rem;font-size:var(--llm-font-size-xs, .75rem)}.llm-btn--secondary[data-v-dd3230e5]{background:var(--llm-bg-mute, #242830);border-color:var(--llm-accent, #6366f1);color:var(--llm-accent, #6366f1)}.llm-btn--ghost[data-v-dd3230e5]{background:transparent;border-color:transparent}.llm-btn--ghost[data-v-dd3230e5]:hover:not(:disabled){background:var(--llm-bg-mute, #242830)}.llm-btn--danger[data-v-dd3230e5]{background:var(--llm-error, #ef4444);border-color:var(--llm-error, #ef4444);color:#fff}.llm-form-control[data-v-dd3230e5]{width:100%;padding:.5rem .75rem;border:1px solid var(--llm-border, #33383f);border-radius:var(--llm-radius-sm, 4px);background:var(--llm-bg, #181a1f);color:var(--llm-text, #e6e8ea);font-size:var(--llm-font-size-sm, .85rem)}.llm-form-control[data-v-dd3230e5]:focus{outline:none;border-color:var(--llm-accent, #6366f1)}@media(max-width:768px){.llm-manager-header[data-v-dd3230e5],.llm-manager-footer[data-v-dd3230e5],.llm-scrollable-content[data-v-dd3230e5]{padding:1rem}.llm-key-header[data-v-dd3230e5]{flex-direction:column;align-items:stretch;gap:1rem;padding:.75rem}.llm-key-input-row[data-v-dd3230e5]{flex-direction:column;align-items:stretch}.llm-provider-select[data-v-dd3230e5]{min-width:auto}}@media(max-width:480px){.llm-manager-header h3[data-v-dd3230e5]{font-size:1.1rem}}.llm-modal-overlay[data-v-a26b0717]{position:fixed;top:0;left:0;right:0;bottom:0;background:#000000b3;display:flex;align-items:center;justify-content:center;z-index:1000}.llm-modal[data-v-a26b0717]{background:var(--llm-bg, #181a1f);border:2px solid var(--llm-border, #33383f);border-radius:var(--llm-radius-md, 8px);width:90%;max-width:700px;max-height:90%;position:relative;display:flex;flex-direction:column;color:var(--llm-text, #e6e8ea)}.llm-modal-header[data-v-a26b0717]{display:flex;justify-content:space-between;align-items:center;padding:1.5rem;border-bottom:1px solid var(--llm-border, #33383f);flex-shrink:0}.llm-modal-header h2[data-v-a26b0717]{margin:0;color:var(--llm-text, #e6e8ea);font-size:1.25rem}.llm-close-btn[data-v-a26b0717]{font-size:1.5rem;line-height:1}.llm-close-btn[data-v-a26b0717]:hover{opacity:.7}.llm-modal-body[data-v-a26b0717]{padding:1.5rem;flex:1;overflow-y:auto;min-height:0}.llm-btn[data-v-a26b0717]{padding:.5rem 1rem;border-radius:var(--llm-radius-sm, 4px);border:1px solid var(--llm-border, #33383f);background:var(--llm-bg-soft, #1f2228);color:var(--llm-text, #e6e8ea);cursor:pointer;font-size:var(--llm-font-size-sm, .85rem);transition:all .15s ease}.llm-btn[data-v-a26b0717]:hover:not(:disabled){border-color:var(--llm-border-hover, #4b525c);background:var(--llm-bg-mute, #242830)}.llm-btn[data-v-a26b0717]:disabled{opacity:.5;cursor:not-allowed}.llm-btn--sm[data-v-a26b0717]{padding:.25rem .5rem;font-size:var(--llm-font-size-xs, .75rem)}.llm-btn--primary[data-v-a26b0717]{background:var(--llm-accent, #6366f1);border-color:var(--llm-accent, #6366f1);color:var(--llm-bg, #181a1f)}.llm-btn--primary[data-v-a26b0717]:hover:not(:disabled){filter:brightness(1.1)}.llm-btn--secondary[data-v-a26b0717]{background:var(--llm-bg-mute, #242830);border-color:var(--llm-accent, #6366f1);color:var(--llm-accent, #6366f1)}.llm-btn--ghost[data-v-a26b0717]{background:transparent;border-color:transparent}.llm-btn--ghost[data-v-a26b0717]:hover:not(:disabled){background:var(--llm-bg-mute, #242830)}.llm-btn--danger[data-v-a26b0717]{background:var(--llm-error, #ef4444);border-color:var(--llm-error, #ef4444);color:#fff}.llm-btn--danger[data-v-a26b0717]:hover:not(:disabled){filter:brightness(1.1)}.llm-form-control[data-v-a26b0717]{width:100%;padding:.5rem .75rem;border:1px solid var(--llm-border, #33383f);border-radius:var(--llm-radius-sm, 4px);background:var(--llm-bg, #181a1f);color:var(--llm-text, #e6e8ea);font-size:var(--llm-font-size-sm, .85rem)}.llm-form-control[data-v-a26b0717]:focus{outline:none;border-color:var(--llm-accent, #6366f1)}.llm-form-control[data-v-a26b0717]:disabled{opacity:.6;cursor:not-allowed}.llm-provider-list[data-v-a26b0717]{margin-bottom:2rem;padding-bottom:1.5rem}.llm-provider-list[data-v-a26b0717]:not(:last-child){border-bottom:1px solid var(--llm-border, #33383f)}.llm-provider-list h3[data-v-a26b0717]{margin:0 0 1rem;color:var(--llm-text, #e6e8ea);font-size:1rem}.llm-provider-item[data-v-a26b0717]{display:flex;justify-content:space-between;align-items:center;padding:1rem;border:1px solid var(--llm-border, #33383f);border-radius:var(--llm-radius-sm, 4px);margin-bottom:.5rem;background:var(--llm-bg-soft, #1f2228);cursor:pointer;transition:all .15s ease;position:relative}.llm-provider-item[data-v-a26b0717]:hover{background:var(--llm-bg-mute, #242830);border-color:var(--llm-border-hover, #4b525c)}.llm-provider-item.active[data-v-a26b0717]{border-color:var(--llm-accent, #6366f1);background:var(--llm-bg, #181a1f);box-shadow:0 0 0 1px var(--llm-border, #33383f)}.llm-provider-item.disabled[data-v-a26b0717]{opacity:.6;cursor:not-allowed}.llm-provider-info[data-v-a26b0717]{flex:1}.llm-provider-name-row[data-v-a26b0717]{display:flex;align-items:center;margin-bottom:.25rem}.llm-provider-name[data-v-a26b0717]{font-weight:500;color:var(--llm-text, #e6e8ea);font-size:.95rem}.llm-active-badge[data-v-a26b0717],.llm-judge-badge[data-v-a26b0717]{position:absolute;top:-12px;background:var(--llm-accent, #6366f1);color:var(--llm-bg, #181a1f);font-size:.6rem;font-weight:700;padding:.2rem .5rem;border-radius:3px;text-transform:uppercase;letter-spacing:.5px;z-index:1}.llm-active-badge[data-v-a26b0717]{left:16px}.llm-judge-badge[data-v-a26b0717]{left:80px}.llm-disabled-badge[data-v-a26b0717]{background:var(--llm-border, #33383f);color:var(--llm-text, #e6e8ea);font-size:.65rem;font-weight:500;padding:.1rem .4rem;border-radius:2px;margin-left:.5rem;text-transform:uppercase}.llm-provider-details-row[data-v-a26b0717]{display:flex;align-items:center;gap:.75rem}.llm-test-status-btn[data-v-a26b0717]{background:none;border:1px solid var(--llm-border, #33383f);color:var(--llm-text, #e6e8ea);font-size:.7rem;font-weight:500;padding:.25rem .5rem;border-radius:3px;cursor:pointer;transition:all .15s ease;min-width:60px;text-align:center}.llm-test-status-btn.llm-test-default[data-v-a26b0717]:hover:not(:disabled){background:var(--llm-bg-mute, #242830);border-color:var(--llm-accent, #6366f1)}.llm-test-status-btn.llm-test-testing[data-v-a26b0717]{border-color:var(--llm-border-hover, #4b525c)}.llm-test-status-btn.llm-test-success[data-v-a26b0717]{border-color:var(--llm-success, #10b981);color:var(--llm-success, #10b981)}.llm-test-status-btn.llm-test-failed[data-v-a26b0717]{border-color:var(--llm-error, #ef4444);color:var(--llm-error, #ef4444)}.llm-provider-details[data-v-a26b0717]{font-size:.8rem;color:var(--llm-text-dim, #9aa0a6);flex:1}.llm-provider-actions[data-v-a26b0717]{display:flex;gap:.5rem;align-items:center}.llm-form-grid[data-v-a26b0717]{display:grid;grid-template-columns:auto 1fr;gap:1rem;align-items:center}.llm-field-label[data-v-a26b0717]{margin-bottom:0;white-space:nowrap;color:var(--llm-text, #e6e8ea);font-size:var(--llm-font-size-sm, .85rem)}.llm-api-key-field[data-v-a26b0717]{display:flex;flex-direction:column}.llm-api-key-input-row[data-v-a26b0717]{display:flex;gap:.5rem;align-items:stretch}.llm-api-key-input-row .llm-form-control[data-v-a26b0717]{flex:1}.llm-api-key-field .llm-security-note[data-v-a26b0717]{margin-top:.5rem}.llm-model-selector[data-v-a26b0717]{display:flex;gap:.5rem;align-items:stretch;flex-wrap:wrap}.llm-model-selector .llm-form-control[data-v-a26b0717]{flex:1}.llm-refresh-btn[data-v-a26b0717]{min-width:4rem;flex-shrink:0}.llm-model-error[data-v-a26b0717]{width:100%;font-size:.75rem;color:var(--llm-error, #ef4444)}.llm-thinking-toggle[data-v-a26b0717]{display:block;width:100%;margin-top:.5rem}.llm-temperature-field[data-v-a26b0717]{display:flex;flex-direction:column}.llm-field-note[data-v-a26b0717]{display:block;margin-top:.5rem;font-size:.75rem;color:var(--llm-text-dim, #9aa0a6);font-style:italic}.llm-checkbox-label[data-v-a26b0717]{display:flex;align-items:center;gap:.5rem;cursor:pointer;font-weight:500;color:var(--llm-text, #e6e8ea);font-size:var(--llm-font-size-sm, .85rem)}.llm-checkbox[data-v-a26b0717]{width:1rem;height:1rem;cursor:pointer}.llm-security-note[data-v-a26b0717]{display:block;font-size:.75rem;color:var(--llm-text-dim, #9aa0a6)}.llm-modal-footer[data-v-a26b0717]{display:flex;align-items:center;gap:.5rem;padding:1.5rem;border-top:1px solid var(--llm-border, #33383f);flex-shrink:0}.llm-footer-spacer[data-v-a26b0717]{flex:1}.llm-empty-state[data-v-a26b0717]{text-align:center;padding:2rem;color:var(--llm-text-dim, #9aa0a6)}.llm-empty-state p[data-v-a26b0717]{margin:.5rem 0;font-size:.9rem}.llm-keys-manager-overlay[data-v-a26b0717]{position:fixed;top:0;left:0;right:0;bottom:0;background:#000c;display:flex;align-items:center;justify-content:center;z-index:1001}.llm-keys-manager-content[data-v-a26b0717]{width:90%;max-width:600px;max-height:80vh;border:2px solid var(--llm-border, #33383f);border-radius:var(--llm-radius-md, 8px);overflow:hidden}@media(max-width:768px){.llm-modal[data-v-a26b0717]{width:95%;margin:1rem}.llm-modal-body[data-v-a26b0717],.llm-modal-header[data-v-a26b0717],.llm-modal-footer[data-v-a26b0717]{padding:1rem}.llm-provider-item[data-v-a26b0717]{flex-direction:column;align-items:stretch;gap:.75rem;padding:.75rem}.llm-provider-actions[data-v-a26b0717]{width:100%;justify-content:flex-end}.llm-form-grid[data-v-a26b0717]{grid-template-columns:1fr;gap:.5rem}.llm-field-label[data-v-a26b0717]{padding-top:0;margin-top:.5rem}.llm-active-badge[data-v-a26b0717],.llm-judge-badge[data-v-a26b0717]{top:-8px;font-size:.55rem;padding:.15rem .4rem}.llm-judge-badge[data-v-a26b0717]{left:66px}}@media(max-width:480px){.llm-modal[data-v-a26b0717]{width:98%;margin:.5rem}.llm-modal-body[data-v-a26b0717],.llm-modal-header[data-v-a26b0717],.llm-modal-footer[data-v-a26b0717]{padding:.75rem}}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@anvaka/vue-llm",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Browser-only LLM client with provider adapters and Vue 3 components",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"module": "dist/index.js",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "vite build",
|
|
11
|
+
"dev": "vite build --watch"
|
|
12
|
+
},
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"import": "./dist/index.js",
|
|
16
|
+
"default": "./dist/index.js"
|
|
17
|
+
},
|
|
18
|
+
"./vue": {
|
|
19
|
+
"import": "./dist/vue/index.js",
|
|
20
|
+
"default": "./dist/vue/index.js"
|
|
21
|
+
},
|
|
22
|
+
"./providers": {
|
|
23
|
+
"import": "./dist/providers/index.js",
|
|
24
|
+
"default": "./dist/providers/index.js"
|
|
25
|
+
},
|
|
26
|
+
"./styles/variables.css": "./src/styles/variables.css",
|
|
27
|
+
"./styles/components.css": "./src/styles/components.css"
|
|
28
|
+
|
|
29
|
+
},
|
|
30
|
+
"peerDependencies": {
|
|
31
|
+
"vue": ">=3.3.0"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"vite": "^6.0.0",
|
|
35
|
+
"@vitejs/plugin-vue": "^5.2.0"
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist",
|
|
39
|
+
"src/styles"
|
|
40
|
+
]
|
|
41
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
.llm-btn {
|
|
2
|
+
padding: var(--llm-spacing-xs, 0.25rem) var(--llm-spacing-sm, 0.5rem);
|
|
3
|
+
background: transparent;
|
|
4
|
+
border: 1px solid var(--llm-border, #444);
|
|
5
|
+
border-radius: var(--llm-radius-sm, 4px);
|
|
6
|
+
color: var(--llm-text, #ddd);
|
|
7
|
+
}
|
|
8
|
+
.llm-btn:hover { border-color: var(--llm-border-hover, #666); }
|
|
9
|
+
.llm-input {
|
|
10
|
+
padding: var(--llm-spacing-xs, 0.25rem) var(--llm-spacing-sm, 0.5rem);
|
|
11
|
+
border: 1px solid var(--llm-border, #444);
|
|
12
|
+
border-radius: var(--llm-radius-sm, 4px);
|
|
13
|
+
background: var(--llm-bg, #111);
|
|
14
|
+
color: var(--llm-text, #ddd);
|
|
15
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @anvaka/vue-llm CSS Variables
|
|
3
|
+
*
|
|
4
|
+
* Override these in your app to customize the look of LLM components.
|
|
5
|
+
* All components use these variables with sensible defaults.
|
|
6
|
+
*/
|
|
7
|
+
:root {
|
|
8
|
+
/* Background colors */
|
|
9
|
+
--llm-bg: #181a1f;
|
|
10
|
+
--llm-bg-soft: #1f2228;
|
|
11
|
+
--llm-bg-mute: #242830;
|
|
12
|
+
|
|
13
|
+
/* Text colors */
|
|
14
|
+
--llm-text: #e6e8ea;
|
|
15
|
+
--llm-text-dim: #9aa0a6;
|
|
16
|
+
|
|
17
|
+
/* Border colors */
|
|
18
|
+
--llm-border: #33383f;
|
|
19
|
+
--llm-border-hover: #4b525c;
|
|
20
|
+
|
|
21
|
+
/* Accent/primary color */
|
|
22
|
+
--llm-accent: #6366f1;
|
|
23
|
+
--llm-accent-contrast: #ffffff;
|
|
24
|
+
|
|
25
|
+
/* Status colors */
|
|
26
|
+
--llm-error: #ef4444;
|
|
27
|
+
--llm-warn: #f59e0b;
|
|
28
|
+
--llm-success: #10b981;
|
|
29
|
+
|
|
30
|
+
/* Typography */
|
|
31
|
+
--llm-font-size-xs: 0.7rem;
|
|
32
|
+
--llm-font-size-sm: 0.85rem;
|
|
33
|
+
--llm-font-size-base: 1rem;
|
|
34
|
+
|
|
35
|
+
/* Spacing */
|
|
36
|
+
--llm-spacing-xs: 0.25rem;
|
|
37
|
+
--llm-spacing-sm: 0.5rem;
|
|
38
|
+
--llm-spacing-md: 0.75rem;
|
|
39
|
+
--llm-spacing-lg: 1rem;
|
|
40
|
+
|
|
41
|
+
/* Border radius */
|
|
42
|
+
--llm-radius-sm: 4px;
|
|
43
|
+
--llm-radius-md: 6px;
|
|
44
|
+
--llm-radius-lg: 8px;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* Light theme support */
|
|
48
|
+
html[data-theme='light'],
|
|
49
|
+
[data-theme='light'] {
|
|
50
|
+
--llm-bg: #ffffff;
|
|
51
|
+
--llm-bg-soft: #f5f7fa;
|
|
52
|
+
--llm-bg-mute: #eef1f5;
|
|
53
|
+
--llm-text: #1d2125;
|
|
54
|
+
--llm-text-dim: #5e6b77;
|
|
55
|
+
--llm-border: #d3d8df;
|
|
56
|
+
--llm-border-hover: #b5bcc5;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* Respect system preference */
|
|
60
|
+
@media (prefers-color-scheme: light) {
|
|
61
|
+
:root:not([data-theme='dark']) {
|
|
62
|
+
--llm-bg: #ffffff;
|
|
63
|
+
--llm-bg-soft: #f5f7fa;
|
|
64
|
+
--llm-bg-mute: #eef1f5;
|
|
65
|
+
--llm-text: #1d2125;
|
|
66
|
+
--llm-text-dim: #5e6b77;
|
|
67
|
+
--llm-border: #d3d8df;
|
|
68
|
+
--llm-border-hover: #b5bcc5;
|
|
69
|
+
}
|
|
70
|
+
}
|