@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.
@@ -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
+ };
@@ -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
+ }