@seer-ai/seer-vue 0.1.0-beta.4 → 0.1.0-beta.5

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 CHANGED
@@ -13,6 +13,7 @@ npm install @seer-ai/seer-vue@beta
13
13
  ```ts
14
14
  import { createApp } from 'vue'
15
15
  import { SeerChatPlugin } from '@seer-ai/seer-vue'
16
+ import '@seer-ai/seer-vue/style.css' // 必须:链接的蓝色/下划线样式来自这里
16
17
  import App from './App.vue'
17
18
 
18
19
  createApp(App)
@@ -37,10 +38,14 @@ import { SeerChat } from '@seer-ai/seer-vue'
37
38
 
38
39
  ## Use custom UI
39
40
 
41
+ 自定义 UI 时**不要**直接把 `message.content` 当纯文本渲染——回复里的下载/截图链接是裸
42
+ URL 文本,会原样显示成一长串 url。用导出的 `toContentLines` 把它切成可点链接(这正是
43
+ `<SeerChat>` 内部用的逻辑):
44
+
40
45
  ```vue
41
46
  <script setup lang="ts">
42
47
  import { ref } from 'vue'
43
- import { useSeerChat } from '@seer-ai/seer-vue'
48
+ import { toContentLines, useSeerChat } from '@seer-ai/seer-vue'
44
49
 
45
50
  const input = ref('')
46
51
  const chat = useSeerChat()
@@ -52,6 +57,19 @@ async function submit() {
52
57
  await chat.send(content)
53
58
  }
54
59
  </script>
60
+
61
+ <template>
62
+ <div v-for="(message, i) in chat.messages.value" :key="i">
63
+ <p v-for="(line, li) in toContentLines(message.content)" :key="li">
64
+ <template v-for="(seg, si) in line" :key="si">
65
+ <a v-if="seg.kind === 'link'" :href="seg.href" target="_blank" rel="noopener noreferrer">{{
66
+ seg.label
67
+ }}</a>
68
+ <span v-else>{{ seg.value }}</span>
69
+ </template>
70
+ </p>
71
+ </div>
72
+ </template>
55
73
  ```
56
74
 
57
75
  ## Security
@@ -0,0 +1,11 @@
1
+ export type ContentSegment = {
2
+ kind: 'text';
3
+ value: string;
4
+ } | {
5
+ kind: 'link';
6
+ href: string;
7
+ label: string;
8
+ };
9
+ export declare function linkAnchorLabel(prefix: string): string | null;
10
+ export declare function toContentSegments(content: string): ContentSegment[];
11
+ export declare function toContentLines(content: string): ContentSegment[][];
package/dist/index.css CHANGED
@@ -1 +1 @@
1
- .seer-chat[data-v-fefeb009]{display:grid;gap:18px;max-width:720px;min-height:640px;padding:0;overflow:hidden;color:#18212f;background:radial-gradient(circle at 18% 0%,rgba(71,106,255,.14),transparent 32%),linear-gradient(180deg,#fff,#f7f9fd);border:1px solid rgba(127,143,170,.28);border-radius:28px;box-shadow:0 24px 70px #151f3429}.seer-chat__header[data-v-fefeb009]{display:flex;gap:16px;align-items:flex-start;justify-content:space-between;padding:22px 22px 18px;color:#fff;background:linear-gradient(135deg,#141f34f5,#243f76f0),#172033}.seer-chat__header h2[data-v-fefeb009]{margin:0;font-size:22px;letter-spacing:-.03em}.seer-chat__header p[data-v-fefeb009],.seer-chat__empty[data-v-fefeb009],.seer-chat__status[data-v-fefeb009]{margin:4px 0 0;color:#5f6f89;font-size:14px}.seer-chat__header p[data-v-fefeb009]{max-width:34ch;color:#ffffffb8}.seer-chat__messages[data-v-fefeb009]{display:grid;gap:12px;align-content:start;min-height:330px;max-height:470px;padding:18px 18px 4px;overflow-y:auto}.seer-chat__empty[data-v-fefeb009]{display:grid;place-items:center;min-height:260px;padding:0 28px;color:#66748a;text-align:center;border:1px dashed rgba(127,143,170,.38);border-radius:22px;background:#ffffff9e}.seer-chat__message[data-v-fefeb009]{display:grid;gap:6px;max-width:min(86%,520px);padding:13px 15px;border:1px solid rgba(127,143,170,.16);border-radius:18px 18px 18px 6px;background:#ffffffe0;box-shadow:0 12px 28px #151f3414}.seer-chat__message--user[data-v-fefeb009]{justify-self:end;color:#fff;border-color:transparent;border-radius:18px 18px 6px;background:linear-gradient(135deg,#285cff,#1d45c9);box-shadow:0 14px 30px #1f5eff3d}.seer-chat__role[data-v-fefeb009]{font-size:12px;font-weight:700;opacity:.75}.seer-chat__body[data-v-fefeb009]{display:flex;flex-direction:column}.seer-chat__line[data-v-fefeb009]{margin:0;white-space:pre-wrap;overflow-wrap:anywhere}.seer-chat__line[data-v-fefeb009]:not(:last-child){margin-bottom:5px}.seer-chat__line[data-v-fefeb009]:empty{height:7px}.seer-chat__link[data-v-fefeb009]{color:#1f5eff;font-weight:600;text-decoration:underline;text-underline-offset:2px;overflow-wrap:anywhere}.seer-chat__link[data-v-fefeb009]:hover{color:#173fb3}.seer-chat__message--user .seer-chat__link[data-v-fefeb009]{color:#fff}.seer-chat__error[data-v-fefeb009],.seer-chat__handoff[data-v-fefeb009]{margin:0 18px;padding:10px 12px;border-radius:14px}.seer-chat__error[data-v-fefeb009]{color:#8a1f11;background:#fff0ed}.seer-chat__handoff[data-v-fefeb009]{color:#704600;background:#fff7db}.seer-chat__form[data-v-fefeb009]{display:grid;gap:11px;margin:0 18px 18px;padding:14px;background:#fff;border:1px solid rgba(127,143,170,.26);border-radius:22px;box-shadow:0 18px 38px #151f3417}.seer-chat__label[data-v-fefeb009]{font-size:13px;font-weight:700}.seer-chat textarea[data-v-fefeb009]{width:100%;box-sizing:border-box;min-height:92px;padding:14px;color:inherit;line-height:1.55;background:#f8fafd;border:1px solid transparent;border-radius:16px;outline:none;resize:vertical;transition:background .16s ease,border-color .16s ease,box-shadow .16s ease}.seer-chat button[data-v-fefeb009]{color:inherit;font-weight:700;border:0;cursor:pointer;transition:transform .16s ease,box-shadow .16s ease,opacity .16s ease}.seer-chat button[data-v-fefeb009]:not(:disabled):hover{transform:translateY(-1px)}.seer-chat button[data-v-fefeb009]:disabled{cursor:not-allowed;opacity:.5}.seer-chat button[data-v-fefeb009]:focus-visible,.seer-chat textarea[data-v-fefeb009]:focus-visible{border-color:#1f5eff6b;box-shadow:0 0 0 4px #1f5eff21}.seer-chat__clear-button[data-v-fefeb009]{flex:0 0 auto;padding:8px 11px;color:#ffffffc7;font-size:12px;background:transparent;border:1px solid rgba(255,255,255,.18)!important;border-radius:12px}.seer-chat__clear-button[data-v-fefeb009]:not(:disabled):hover{color:#fff;background:#ffffff1a;box-shadow:none}.seer-chat__stop-button[data-v-fefeb009]{padding:8px 11px;color:#6c3b12;font-size:12px;background:#fff1df;border-radius:12px}.seer-chat__send-button[data-v-fefeb009]{display:inline-flex;gap:8px;align-items:center;justify-content:center;min-width:86px;padding:10px 14px;color:#fff;background:linear-gradient(135deg,#1f5eff,#173fb3);border-radius:14px;box-shadow:0 12px 24px #1f5eff38}.seer-chat__send-button[data-v-fefeb009]:not(:disabled):hover{box-shadow:0 14px 28px #1f5eff47}.seer-chat__hint[data-v-fefeb009]{margin-right:auto;color:#7a879a;font-size:12px}.seer-chat__composer-footer[data-v-fefeb009]{display:flex;gap:8px;align-items:center;justify-content:flex-end}
1
+ .seer-chat[data-v-f2b8b91f]{display:grid;gap:18px;max-width:720px;min-height:640px;padding:0;overflow:hidden;color:#18212f;background:radial-gradient(circle at 18% 0%,rgba(71,106,255,.14),transparent 32%),linear-gradient(180deg,#fff,#f7f9fd);border:1px solid rgba(127,143,170,.28);border-radius:28px;box-shadow:0 24px 70px #151f3429}.seer-chat__header[data-v-f2b8b91f]{display:flex;gap:16px;align-items:flex-start;justify-content:space-between;padding:22px 22px 18px;color:#fff;background:linear-gradient(135deg,#141f34f5,#243f76f0),#172033}.seer-chat__header h2[data-v-f2b8b91f]{margin:0;font-size:22px;letter-spacing:-.03em}.seer-chat__header p[data-v-f2b8b91f],.seer-chat__empty[data-v-f2b8b91f],.seer-chat__status[data-v-f2b8b91f]{margin:4px 0 0;color:#5f6f89;font-size:14px}.seer-chat__header p[data-v-f2b8b91f]{max-width:34ch;color:#ffffffb8}.seer-chat__messages[data-v-f2b8b91f]{display:grid;gap:12px;align-content:start;min-height:330px;max-height:470px;padding:18px 18px 4px;overflow-y:auto}.seer-chat__empty[data-v-f2b8b91f]{display:grid;place-items:center;min-height:260px;padding:0 28px;color:#66748a;text-align:center;border:1px dashed rgba(127,143,170,.38);border-radius:22px;background:#ffffff9e}.seer-chat__message[data-v-f2b8b91f]{display:grid;gap:6px;max-width:min(86%,520px);padding:13px 15px;border:1px solid rgba(127,143,170,.16);border-radius:18px 18px 18px 6px;background:#ffffffe0;box-shadow:0 12px 28px #151f3414}.seer-chat__message--user[data-v-f2b8b91f]{justify-self:end;color:#fff;border-color:transparent;border-radius:18px 18px 6px;background:linear-gradient(135deg,#285cff,#1d45c9);box-shadow:0 14px 30px #1f5eff3d}.seer-chat__role[data-v-f2b8b91f]{font-size:12px;font-weight:700;opacity:.75}.seer-chat__body[data-v-f2b8b91f]{display:flex;flex-direction:column}.seer-chat__line[data-v-f2b8b91f]{margin:0;white-space:pre-wrap;overflow-wrap:anywhere}.seer-chat__line[data-v-f2b8b91f]:not(:last-child){margin-bottom:5px}.seer-chat__line[data-v-f2b8b91f]:empty{height:7px}.seer-chat__link[data-v-f2b8b91f]{color:#1f5eff;font-weight:600;text-decoration:underline;text-underline-offset:2px;overflow-wrap:anywhere}.seer-chat__link[data-v-f2b8b91f]:hover{color:#173fb3}.seer-chat__message--user .seer-chat__link[data-v-f2b8b91f]{color:#fff}.seer-chat__error[data-v-f2b8b91f],.seer-chat__handoff[data-v-f2b8b91f]{margin:0 18px;padding:10px 12px;border-radius:14px}.seer-chat__error[data-v-f2b8b91f]{color:#8a1f11;background:#fff0ed}.seer-chat__handoff[data-v-f2b8b91f]{color:#704600;background:#fff7db}.seer-chat__form[data-v-f2b8b91f]{display:grid;gap:11px;margin:0 18px 18px;padding:14px;background:#fff;border:1px solid rgba(127,143,170,.26);border-radius:22px;box-shadow:0 18px 38px #151f3417}.seer-chat__label[data-v-f2b8b91f]{font-size:13px;font-weight:700}.seer-chat textarea[data-v-f2b8b91f]{width:100%;box-sizing:border-box;min-height:92px;padding:14px;color:inherit;line-height:1.55;background:#f8fafd;border:1px solid transparent;border-radius:16px;outline:none;resize:vertical;transition:background .16s ease,border-color .16s ease,box-shadow .16s ease}.seer-chat button[data-v-f2b8b91f]{color:inherit;font-weight:700;border:0;cursor:pointer;transition:transform .16s ease,box-shadow .16s ease,opacity .16s ease}.seer-chat button[data-v-f2b8b91f]:not(:disabled):hover{transform:translateY(-1px)}.seer-chat button[data-v-f2b8b91f]:disabled{cursor:not-allowed;opacity:.5}.seer-chat button[data-v-f2b8b91f]:focus-visible,.seer-chat textarea[data-v-f2b8b91f]:focus-visible{border-color:#1f5eff6b;box-shadow:0 0 0 4px #1f5eff21}.seer-chat__clear-button[data-v-f2b8b91f]{flex:0 0 auto;padding:8px 11px;color:#ffffffc7;font-size:12px;background:transparent;border:1px solid rgba(255,255,255,.18)!important;border-radius:12px}.seer-chat__clear-button[data-v-f2b8b91f]:not(:disabled):hover{color:#fff;background:#ffffff1a;box-shadow:none}.seer-chat__stop-button[data-v-f2b8b91f]{padding:8px 11px;color:#6c3b12;font-size:12px;background:#fff1df;border-radius:12px}.seer-chat__send-button[data-v-f2b8b91f]{display:inline-flex;gap:8px;align-items:center;justify-content:center;min-width:86px;padding:10px 14px;color:#fff;background:linear-gradient(135deg,#1f5eff,#173fb3);border-radius:14px;box-shadow:0 12px 24px #1f5eff38}.seer-chat__send-button[data-v-f2b8b91f]:not(:disabled):hover{box-shadow:0 14px 28px #1f5eff47}.seer-chat__hint[data-v-f2b8b91f]{margin-right:auto;color:#7a879a;font-size:12px}.seer-chat__composer-footer[data-v-f2b8b91f]{display:flex;gap:8px;align-items:center;justify-content:flex-end}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export { createSeerClient } from './client';
2
+ export { linkAnchorLabel, toContentLines, toContentSegments, type ContentSegment, } from './content';
2
3
  export { SeerChatPlugin } from './plugin';
3
4
  export { useSeerChat } from './useSeerChat';
4
5
  export { default as SeerChat } from './components/SeerChat.vue';
package/dist/index.js CHANGED
@@ -1,196 +1,217 @@
1
- import { hasInjectionContext as $, inject as N, ref as E, defineComponent as q, computed as L, openBlock as v, createElementBlock as m, createElementVNode as f, toDisplayString as T, unref as w, createCommentVNode as x, Fragment as I, renderList as R, normalizeClass as M, withModifiers as O, withDirectives as B, withKeys as K, vModelText as D } from "vue";
2
- function F(t) {
3
- const n = t.fetcher ?? fetch, e = t.model ?? "seer", o = t.now ?? (() => Math.floor(Date.now() / 1e3));
4
- let u, i = 0;
1
+ import { hasInjectionContext as N, inject as q, ref as w, defineComponent as L, computed as M, openBlock as v, createElementBlock as m, createElementVNode as u, toDisplayString as C, unref as _, createCommentVNode as E, Fragment as I, renderList as R, normalizeClass as B, withModifiers as O, withDirectives as K, withKeys as D, vModelText as F } from "vue";
2
+ function U(e) {
3
+ const n = e.fetcher ?? fetch, t = e.model ?? "seer", a = e.now ?? (() => Math.floor(Date.now() / 1e3));
4
+ let l, r = 0;
5
5
  return {
6
- async chat(l, d = {}) {
7
- const a = l.stream ?? !1, s = await p(), r = {
6
+ async chat(s, c = {}) {
7
+ const f = s.stream ?? !1, h = await d(), p = {
8
8
  "Content-Type": "application/json"
9
9
  };
10
- s && (r.Authorization = `Bearer ${s}`);
11
- const h = await n(t.endpoint, {
10
+ h && (p.Authorization = `Bearer ${h}`);
11
+ const g = await n(e.endpoint, {
12
12
  method: "POST",
13
- headers: r,
14
- signal: d.signal,
13
+ headers: p,
14
+ signal: c.signal,
15
15
  body: JSON.stringify({
16
- model: l.model ?? e,
17
- messages: l.messages,
18
- stream: a,
19
- conversation_id: l.conversationId,
20
- metadata: l.metadata
16
+ model: s.model ?? t,
17
+ messages: s.messages,
18
+ stream: f,
19
+ conversation_id: s.conversationId,
20
+ metadata: s.metadata
21
21
  })
22
22
  });
23
- if (!h.ok)
24
- throw new Error(await j(h));
25
- if (a)
26
- return U(h, d.onDelta);
27
- const y = await h.json();
28
- return A(y);
23
+ if (!g.ok)
24
+ throw new Error(await j(g));
25
+ if (f)
26
+ return V(g, c.onDelta);
27
+ const b = await g.json();
28
+ return P(b);
29
29
  }
30
30
  };
31
- async function p() {
32
- if (!t.sessionEndpoint)
31
+ async function d() {
32
+ if (!e.sessionEndpoint)
33
33
  return;
34
- const l = o();
35
- if (u && i - 30 > l)
36
- return u;
37
- const d = await n(t.sessionEndpoint, {
34
+ const s = a();
35
+ if (l && r - 30 > s)
36
+ return l;
37
+ const c = await n(e.sessionEndpoint, {
38
38
  method: "GET",
39
- headers: await _()
39
+ headers: await o()
40
40
  });
41
- if (!d.ok)
42
- throw new Error(await j(d));
43
- const a = await d.json();
44
- return u = a.token, i = a.expires_at, u;
41
+ if (!c.ok)
42
+ throw new Error(await j(c));
43
+ const f = await c.json();
44
+ return l = f.token, r = f.expires_at, l;
45
45
  }
46
- async function _() {
47
- const l = {};
48
- return t.challengeTokenProvider && (l["X-Seer-Challenge-Token"] = await t.challengeTokenProvider()), l;
46
+ async function o() {
47
+ const s = {};
48
+ return e.challengeTokenProvider && (s["X-Seer-Challenge-Token"] = await e.challengeTokenProvider()), s;
49
49
  }
50
50
  }
51
- function A(t) {
52
- var e, o, u;
53
- const n = (e = t.choices) == null ? void 0 : e[0];
51
+ function P(e) {
52
+ var t, a, l;
53
+ const n = (t = e.choices) == null ? void 0 : t[0];
54
54
  return {
55
- content: ((o = n == null ? void 0 : n.message) == null ? void 0 : o.content) ?? ((u = n == null ? void 0 : n.delta) == null ? void 0 : u.content) ?? "",
56
- conversationId: t.conversation_id,
57
- citations: t.citations ?? [],
58
- handoffRequested: t.handoff_requested ?? !1,
55
+ content: ((a = n == null ? void 0 : n.message) == null ? void 0 : a.content) ?? ((l = n == null ? void 0 : n.delta) == null ? void 0 : l.content) ?? "",
56
+ conversationId: e.conversation_id,
57
+ citations: e.citations ?? [],
58
+ handoffRequested: e.handoff_requested ?? !1,
59
59
  finishReason: n == null ? void 0 : n.finish_reason
60
60
  };
61
61
  }
62
- async function U(t, n) {
63
- var a, s, r;
64
- if (!t.body)
65
- return A({});
66
- const e = t.body.getReader(), o = new TextDecoder();
67
- let u = "", i = "", p, _ = [], l = !1, d;
62
+ async function V(e, n) {
63
+ var f, h, p;
64
+ if (!e.body)
65
+ return P({});
66
+ const t = e.body.getReader(), a = new TextDecoder();
67
+ let l = "", r = "", d, o = [], s = !1, c;
68
68
  for (; ; ) {
69
- const { done: h, value: y } = await e.read();
70
- if (h)
69
+ const { done: g, value: b } = await t.read();
70
+ if (g)
71
71
  break;
72
- u += o.decode(y, { stream: !0 });
73
- const g = u.split(`
72
+ l += a.decode(b, { stream: !0 });
73
+ const T = l.split(`
74
74
 
75
75
  `);
76
- u = g.pop() ?? "";
77
- for (const k of g) {
78
- const b = V(k);
79
- if (!b || b === "[DONE]")
76
+ l = T.pop() ?? "";
77
+ for (const x of T) {
78
+ const S = z(x);
79
+ if (!S || S === "[DONE]")
80
80
  continue;
81
- const S = JSON.parse(b), c = (a = S.choices) == null ? void 0 : a[0], C = ((s = c == null ? void 0 : c.delta) == null ? void 0 : s.content) ?? ((r = c == null ? void 0 : c.message) == null ? void 0 : r.content) ?? "";
82
- C && (i += C, n == null || n(C)), p = S.conversation_id ?? p, _ = S.citations ?? _, l = S.handoff_requested ?? l, d = (c == null ? void 0 : c.finish_reason) ?? d;
81
+ const y = JSON.parse(S), i = (f = y.choices) == null ? void 0 : f[0], k = ((h = i == null ? void 0 : i.delta) == null ? void 0 : h.content) ?? ((p = i == null ? void 0 : i.message) == null ? void 0 : p.content) ?? "";
82
+ k && (r += k, n == null || n(k)), d = y.conversation_id ?? d, o = y.citations ?? o, s = y.handoff_requested ?? s, c = (i == null ? void 0 : i.finish_reason) ?? c;
83
83
  }
84
84
  }
85
85
  return {
86
- content: i,
87
- conversationId: p,
88
- citations: _,
89
- handoffRequested: l,
90
- finishReason: d
86
+ content: r,
87
+ conversationId: d,
88
+ citations: o,
89
+ handoffRequested: s,
90
+ finishReason: c
91
91
  };
92
92
  }
93
- function V(t) {
94
- const n = t.split(`
95
- `).map((e) => e.trim()).filter((e) => e.startsWith("data:")).map((e) => e.slice(5).trim());
93
+ function z(e) {
94
+ const n = e.split(`
95
+ `).map((t) => t.trim()).filter((t) => t.startsWith("data:")).map((t) => t.slice(5).trim());
96
96
  return n.length === 0 ? null : n.join(`
97
97
  `);
98
98
  }
99
- async function j(t) {
100
- let n = t.statusText || "unknown error";
99
+ async function j(e) {
100
+ let n = e.statusText || "unknown error";
101
101
  try {
102
- const e = await t.json();
103
- typeof (e == null ? void 0 : e.detail) == "string" ? n = e.detail : typeof (e == null ? void 0 : e.error) == "string" && (n = e.error);
102
+ const t = await e.json();
103
+ typeof (t == null ? void 0 : t.detail) == "string" ? n = t.detail : typeof (t == null ? void 0 : t.error) == "string" && (n = t.error);
104
104
  } catch {
105
- const e = await t.text().catch(() => "");
106
- e && (n = e);
105
+ const t = await e.text().catch(() => "");
106
+ t && (n = t);
107
107
  }
108
- return `seer chat request failed with status ${t.status}: ${n}`;
108
+ return `seer chat request failed with status ${e.status}: ${n}`;
109
109
  }
110
- const P = Symbol("seer-chat-options"), ce = {
111
- install(t, n) {
112
- t.provide(P, n);
110
+ const A = /https?:\/\/[^\s)]+/g;
111
+ function J(e) {
112
+ return /标签|箱唛/.test(e) ? "点击下载箱唛" : /入库单|交接单/.test(e) ? "点击下载入库单" : /面单/.test(e) ? "点击下载面单" : /截图/.test(e) ? "点击查看报价截图" : /下载链接|labelUrl|下载/.test(e) ? "点击下载" : /链接/.test(e) ? "点击查看" : null;
113
+ }
114
+ function G(e) {
115
+ const n = [];
116
+ A.lastIndex = 0;
117
+ let t = 0, a;
118
+ for (; (a = A.exec(e)) !== null; ) {
119
+ const l = a[0], r = a.index;
120
+ r > t && n.push({ kind: "text", value: e.slice(t, r) });
121
+ const d = e.lastIndexOf(`
122
+ `, r - 1) + 1, o = J(e.slice(d, r)) ?? l;
123
+ n.push({ kind: "link", href: l, label: o }), t = r + l.length;
124
+ }
125
+ return t < e.length && n.push({ kind: "text", value: e.slice(t) }), n;
126
+ }
127
+ function H(e) {
128
+ return e.split(`
129
+ `).map((n) => G(n));
130
+ }
131
+ const $ = Symbol("seer-chat-options"), he = {
132
+ install(e, n) {
133
+ e.provide($, n);
113
134
  }
114
135
  };
115
- function z(t = {}) {
116
- const e = { ...$() ? N(P, void 0) : void 0, ...t };
117
- if (!e.endpoint)
136
+ function W(e = {}) {
137
+ const t = { ...N() ? q($, void 0) : void 0, ...e };
138
+ if (!t.endpoint)
118
139
  throw new Error("Seer chat endpoint is required. Pass it to app.use() or useSeerChat().");
119
- const o = {
120
- endpoint: e.endpoint,
121
- sessionEndpoint: e.sessionEndpoint,
122
- challengeTokenProvider: e.challengeTokenProvider,
123
- model: e.model,
124
- fetcher: e.fetcher
125
- }, u = F(o), i = E([]), p = E(!1), _ = E(null), l = E(), d = E([]), a = E(!1);
126
- let s = null;
127
- async function r(g) {
128
- const k = g.trim();
129
- if (!k || p.value)
140
+ const a = {
141
+ endpoint: t.endpoint,
142
+ sessionEndpoint: t.sessionEndpoint,
143
+ challengeTokenProvider: t.challengeTokenProvider,
144
+ model: t.model,
145
+ fetcher: t.fetcher
146
+ }, l = U(a), r = w([]), d = w(!1), o = w(null), s = w(), c = w([]), f = w(!1);
147
+ let h = null;
148
+ async function p(T) {
149
+ const x = T.trim();
150
+ if (!x || d.value)
130
151
  return;
131
- const b = { role: "user", content: k };
132
- i.value = [...i.value, b], p.value = !0, _.value = null, s = new AbortController();
133
- const S = { role: "assistant", content: "" };
134
- e.stream && (i.value = [...i.value, S]);
152
+ const S = { role: "user", content: x };
153
+ r.value = [...r.value, S], d.value = !0, o.value = null, h = new AbortController();
154
+ const y = { role: "assistant", content: "" };
155
+ t.stream && (r.value = [...r.value, y]);
135
156
  try {
136
- const c = await u.chat(
157
+ const i = await l.chat(
137
158
  {
138
- messages: i.value.filter((C) => C.content),
139
- stream: e.stream ?? !1,
140
- conversationId: l.value
159
+ messages: r.value.filter((k) => k.content),
160
+ stream: t.stream ?? !1,
161
+ conversationId: s.value
141
162
  },
142
163
  {
143
- signal: s.signal,
144
- onDelta(C) {
145
- S.content += C, i.value = [...i.value];
164
+ signal: h.signal,
165
+ onDelta(k) {
166
+ y.content += k, r.value = [...r.value];
146
167
  }
147
168
  }
148
169
  );
149
- e.stream || (i.value = [...i.value, { role: "assistant", content: c.content }]), l.value = c.conversationId, d.value = c.citations, a.value = c.handoffRequested;
150
- } catch (c) {
151
- c.name !== "AbortError" && (_.value = c instanceof Error ? c.message : String(c));
170
+ t.stream || (r.value = [...r.value, { role: "assistant", content: i.content }]), s.value = i.conversationId, c.value = i.citations, f.value = i.handoffRequested;
171
+ } catch (i) {
172
+ i.name !== "AbortError" && (o.value = i instanceof Error ? i.message : String(i));
152
173
  } finally {
153
- p.value = !1, s = null;
174
+ d.value = !1, h = null;
154
175
  }
155
176
  }
156
- function h() {
157
- i.value = [], _.value = null, l.value = void 0, d.value = [], a.value = !1;
177
+ function g() {
178
+ r.value = [], o.value = null, s.value = void 0, c.value = [], f.value = !1;
158
179
  }
159
- function y() {
160
- s == null || s.abort();
180
+ function b() {
181
+ h == null || h.abort();
161
182
  }
162
183
  return {
163
- messages: i,
164
- loading: p,
165
- error: _,
166
- conversationId: l,
167
- citations: d,
168
- handoffRequested: a,
169
- send: r,
170
- reset: h,
171
- abort: y
184
+ messages: r,
185
+ loading: d,
186
+ error: o,
187
+ conversationId: s,
188
+ citations: c,
189
+ handoffRequested: f,
190
+ send: p,
191
+ reset: g,
192
+ abort: b
172
193
  };
173
194
  }
174
- const J = {
195
+ const X = {
175
196
  class: "seer-chat",
176
197
  "aria-label": "多发货 AI 客服聊天"
177
- }, G = { class: "seer-chat__header" }, H = {
198
+ }, Q = { class: "seer-chat__header" }, Y = {
178
199
  class: "seer-chat__messages",
179
200
  "aria-live": "polite"
180
- }, W = {
201
+ }, Z = {
181
202
  key: 0,
182
203
  class: "seer-chat__empty"
183
- }, X = { class: "seer-chat__role" }, Q = { class: "seer-chat__body" }, Y = ["href"], Z = { key: 1 }, ee = {
204
+ }, ee = { class: "seer-chat__role" }, te = { class: "seer-chat__body" }, ne = ["href"], se = { key: 1 }, ae = {
184
205
  key: 0,
185
206
  class: "seer-chat__status"
186
- }, te = {
207
+ }, oe = {
187
208
  key: 1,
188
209
  class: "seer-chat__error",
189
210
  role: "alert"
190
- }, ne = {
211
+ }, re = {
191
212
  key: 2,
192
213
  class: "seer-chat__handoff"
193
- }, se = ["placeholder", "disabled", "onKeydown"], ae = { class: "seer-chat__composer-footer" }, oe = ["disabled"], re = /* @__PURE__ */ q({
214
+ }, le = ["placeholder", "disabled", "onKeydown"], ie = { class: "seer-chat__composer-footer" }, ce = ["disabled"], ue = /* @__PURE__ */ L({
194
215
  __name: "SeerChat",
195
216
  props: {
196
217
  endpoint: {},
@@ -200,128 +221,111 @@ const J = {
200
221
  title: { default: "多发货 AI 客服" },
201
222
  placeholder: { default: "输入你的问题,例如:美国海派怎么报价?" }
202
223
  },
203
- setup(t) {
204
- const n = t, e = E(""), o = z({
224
+ setup(e) {
225
+ const n = e, t = w(""), a = W({
205
226
  endpoint: n.endpoint,
206
227
  model: n.model,
207
228
  stream: n.stream,
208
229
  fetcher: n.fetcher
209
- }), u = L(
210
- () => e.value.trim().length > 0 && !o.loading.value
211
- ), i = /https?:\/\/[^\s)]+/g;
212
- function p(a) {
213
- return /标签|箱唛/.test(a) ? "点击下载箱唛" : /入库单|交接单/.test(a) ? "点击下载入库单" : /面单/.test(a) ? "点击下载面单" : /截图/.test(a) ? "点击查看报价截图" : /下载链接|labelUrl|下载/.test(a) ? "点击下载" : /链接/.test(a) ? "点击查看" : null;
214
- }
215
- function _(a) {
216
- const s = [];
217
- i.lastIndex = 0;
218
- let r = 0, h;
219
- for (; (h = i.exec(a)) !== null; ) {
220
- const y = h[0], g = h.index;
221
- g > r && s.push({ kind: "text", value: a.slice(r, g) });
222
- const k = a.lastIndexOf(`
223
- `, g - 1) + 1, b = p(a.slice(k, g)) ?? y;
224
- s.push({ kind: "link", href: y, label: b }), r = g + y.length;
225
- }
226
- return r < a.length && s.push({ kind: "text", value: a.slice(r) }), s;
227
- }
228
- function l(a) {
229
- return a.split(`
230
- `).map((s) => _(s));
231
- }
232
- async function d() {
233
- if (!u.value)
230
+ }), l = M(
231
+ () => t.value.trim().length > 0 && !a.loading.value
232
+ );
233
+ async function r() {
234
+ if (!l.value)
234
235
  return;
235
- const a = e.value;
236
- e.value = "", await o.send(a);
236
+ const d = t.value;
237
+ t.value = "", await a.send(d);
237
238
  }
238
- return (a, s) => (v(), m("section", J, [
239
- f("header", G, [
240
- f("div", null, [
241
- f("h2", null, T(t.title), 1),
242
- s[3] || (s[3] = f("p", null, "可咨询渠道报价、时效、下单资料、入仓和售后问题。", -1))
239
+ return (d, o) => (v(), m("section", X, [
240
+ u("header", Q, [
241
+ u("div", null, [
242
+ u("h2", null, C(e.title), 1),
243
+ o[3] || (o[3] = u("p", null, "可咨询渠道报价、时效、下单资料、入仓和售后问题。", -1))
243
244
  ]),
244
- f("button", {
245
+ u("button", {
245
246
  type: "button",
246
247
  class: "seer-chat__clear-button",
247
- onClick: s[0] || (s[0] = (r) => w(o).reset())
248
+ onClick: o[0] || (o[0] = (s) => _(a).reset())
248
249
  }, " 清空对话 ")
249
250
  ]),
250
- f("div", H, [
251
- w(o).messages.value.length === 0 ? (v(), m("p", W, " 直接输入问题即可,复杂查单或赔付问题会提示转人工。 ")) : x("", !0),
252
- (v(!0), m(I, null, R(w(o).messages.value, (r, h) => (v(), m("article", {
253
- key: `${r.role}-${h}`,
254
- class: M(["seer-chat__message", `seer-chat__message--${r.role}`])
251
+ u("div", Y, [
252
+ _(a).messages.value.length === 0 ? (v(), m("p", Z, " 直接输入问题即可,复杂查单或赔付问题会提示转人工。 ")) : E("", !0),
253
+ (v(!0), m(I, null, R(_(a).messages.value, (s, c) => (v(), m("article", {
254
+ key: `${s.role}-${c}`,
255
+ class: B(["seer-chat__message", `seer-chat__message--${s.role}`])
255
256
  }, [
256
- f("span", X, T(r.role === "user" ? "你" : "AI 客服"), 1),
257
- f("div", Q, [
258
- (v(!0), m(I, null, R(l(r.content), (y, g) => (v(), m("p", {
259
- key: g,
257
+ u("span", ee, C(s.role === "user" ? "你" : "AI 客服"), 1),
258
+ u("div", te, [
259
+ (v(!0), m(I, null, R(_(H)(s.content), (f, h) => (v(), m("p", {
260
+ key: h,
260
261
  class: "seer-chat__line"
261
262
  }, [
262
- (v(!0), m(I, null, R(y, (k, b) => (v(), m(I, { key: b }, [
263
- k.kind === "link" ? (v(), m("a", {
263
+ (v(!0), m(I, null, R(f, (p, g) => (v(), m(I, { key: g }, [
264
+ p.kind === "link" ? (v(), m("a", {
264
265
  key: 0,
265
266
  class: "seer-chat__link",
266
- href: k.href,
267
+ href: p.href,
267
268
  target: "_blank",
268
269
  rel: "noopener noreferrer"
269
- }, T(k.label), 9, Y)) : (v(), m("span", Z, T(k.value), 1))
270
+ }, C(p.label), 9, ne)) : (v(), m("span", se, C(p.value), 1))
270
271
  ], 64))), 128))
271
272
  ]))), 128))
272
273
  ])
273
274
  ], 2))), 128))
274
275
  ]),
275
- w(o).loading.value ? (v(), m("p", ee, "正在回复...")) : x("", !0),
276
- w(o).error.value ? (v(), m("p", te, T(w(o).error.value), 1)) : x("", !0),
277
- w(o).handoffRequested.value ? (v(), m("p", ne, " 建议转人工:请留下联系方式或等待客服接入。 ")) : x("", !0),
278
- f("form", {
276
+ _(a).loading.value ? (v(), m("p", ae, "正在回复...")) : E("", !0),
277
+ _(a).error.value ? (v(), m("p", oe, C(_(a).error.value), 1)) : E("", !0),
278
+ _(a).handoffRequested.value ? (v(), m("p", re, " 建议转人工:请留下联系方式或等待客服接入。 ")) : E("", !0),
279
+ u("form", {
279
280
  class: "seer-chat__form",
280
- onSubmit: O(d, ["prevent"])
281
+ onSubmit: O(r, ["prevent"])
281
282
  }, [
282
- s[6] || (s[6] = f("label", {
283
+ o[6] || (o[6] = u("label", {
283
284
  class: "seer-chat__label",
284
285
  for: "seer-chat-input"
285
286
  }, "问题", -1)),
286
- B(f("textarea", {
287
+ K(u("textarea", {
287
288
  id: "seer-chat-input",
288
- "onUpdate:modelValue": s[1] || (s[1] = (r) => e.value = r),
289
- placeholder: t.placeholder,
290
- disabled: w(o).loading.value,
289
+ "onUpdate:modelValue": o[1] || (o[1] = (s) => t.value = s),
290
+ placeholder: e.placeholder,
291
+ disabled: _(a).loading.value,
291
292
  rows: "3",
292
- onKeydown: K(O(d, ["exact", "prevent"]), ["enter"])
293
- }, null, 40, se), [
294
- [D, e.value]
293
+ onKeydown: D(O(r, ["exact", "prevent"]), ["enter"])
294
+ }, null, 40, le), [
295
+ [F, t.value]
295
296
  ]),
296
- f("div", ae, [
297
- s[5] || (s[5] = f("span", { class: "seer-chat__hint" }, "Enter 发送 · Shift+Enter 换行", -1)),
298
- w(o).loading.value ? (v(), m("button", {
297
+ u("div", ie, [
298
+ o[5] || (o[5] = u("span", { class: "seer-chat__hint" }, "Enter 发送 · Shift+Enter 换行", -1)),
299
+ _(a).loading.value ? (v(), m("button", {
299
300
  key: 0,
300
301
  type: "button",
301
302
  class: "seer-chat__stop-button",
302
- onClick: s[2] || (s[2] = (r) => w(o).abort())
303
- }, " 停止 ")) : x("", !0),
304
- f("button", {
303
+ onClick: o[2] || (o[2] = (s) => _(a).abort())
304
+ }, " 停止 ")) : E("", !0),
305
+ u("button", {
305
306
  class: "seer-chat__send-button",
306
307
  type: "submit",
307
- disabled: !u.value
308
- }, [...s[4] || (s[4] = [
309
- f("span", null, "发送", -1),
310
- f("span", { "aria-hidden": "true" }, "↵", -1)
311
- ])], 8, oe)
308
+ disabled: !l.value
309
+ }, [...o[4] || (o[4] = [
310
+ u("span", null, "发送", -1),
311
+ u("span", { "aria-hidden": "true" }, "↵", -1)
312
+ ])], 8, ce)
312
313
  ])
313
314
  ], 32)
314
315
  ]));
315
316
  }
316
- }), le = (t, n) => {
317
- const e = t.__vccOpts || t;
318
- for (const [o, u] of n)
319
- e[o] = u;
320
- return e;
321
- }, ue = /* @__PURE__ */ le(re, [["__scopeId", "data-v-fefeb009"]]);
317
+ }), de = (e, n) => {
318
+ const t = e.__vccOpts || e;
319
+ for (const [a, l] of n)
320
+ t[a] = l;
321
+ return t;
322
+ }, ve = /* @__PURE__ */ de(ue, [["__scopeId", "data-v-f2b8b91f"]]);
322
323
  export {
323
- ue as SeerChat,
324
- ce as SeerChatPlugin,
325
- F as createSeerClient,
326
- z as useSeerChat
324
+ ve as SeerChat,
325
+ he as SeerChatPlugin,
326
+ U as createSeerClient,
327
+ J as linkAnchorLabel,
328
+ H as toContentLines,
329
+ G as toContentSegments,
330
+ W as useSeerChat
327
331
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seer-ai/seer-vue",
3
- "version": "0.1.0-beta.4",
3
+ "version": "0.1.0-beta.5",
4
4
  "description": "Vue chat plugin for the seer OpenAI-compatible API",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",