opc-agent 1.1.3 → 1.2.1
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/CHANGELOG.md +6 -0
- package/CONTRIBUTING.md +75 -75
- package/README.md +429 -429
- package/README.zh-CN.md +415 -415
- package/dist/channels/web.js +256 -256
- package/dist/core/streaming.d.ts +56 -0
- package/dist/core/streaming.js +160 -0
- package/dist/deploy/hermes.js +22 -22
- package/dist/deploy/openclaw.js +31 -31
- package/dist/index.d.ts +4 -0
- package/dist/index.js +7 -1
- package/dist/providers/index.d.ts +1 -1
- package/dist/providers/index.js +13 -148
- package/dist/schema/oad.d.ts +3 -3
- package/dist/templates/code-reviewer.js +5 -5
- package/dist/templates/customer-service.js +2 -2
- package/dist/templates/data-analyst.js +5 -5
- package/dist/templates/knowledge-base.js +2 -2
- package/dist/templates/sales-assistant.js +4 -4
- package/dist/templates/teacher.js +6 -6
- package/dist/tools/gateway.d.ts +28 -0
- package/dist/tools/gateway.js +177 -0
- package/docs/.vitepress/config.ts +103 -103
- package/docs/api/cli.md +48 -48
- package/docs/api/oad-schema.md +64 -64
- package/docs/api/sdk.md +80 -80
- package/docs/guide/concepts.md +51 -51
- package/docs/guide/configuration.md +79 -79
- package/docs/guide/deployment.md +42 -42
- package/docs/guide/getting-started.md +44 -44
- package/docs/guide/templates.md +28 -28
- package/docs/guide/testing.md +84 -84
- package/docs/index.md +27 -27
- package/docs/zh/api/cli.md +54 -54
- package/docs/zh/api/oad-schema.md +87 -87
- package/docs/zh/api/sdk.md +102 -102
- package/docs/zh/guide/concepts.md +104 -104
- package/docs/zh/guide/configuration.md +135 -135
- package/docs/zh/guide/deployment.md +81 -81
- package/docs/zh/guide/getting-started.md +82 -82
- package/docs/zh/guide/templates.md +84 -84
- package/docs/zh/guide/testing.md +88 -88
- package/docs/zh/index.md +27 -27
- package/examples/customer-service-demo/README.md +90 -90
- package/examples/customer-service-demo/oad.yaml +107 -107
- package/package.json +1 -1
- package/src/analytics/index.ts +66 -66
- package/src/channels/discord.ts +192 -192
- package/src/channels/email.ts +177 -177
- package/src/channels/feishu.ts +236 -236
- package/src/channels/index.ts +15 -15
- package/src/channels/slack.ts +160 -160
- package/src/channels/telegram.ts +90 -90
- package/src/channels/voice.ts +106 -106
- package/src/channels/web.ts +17 -17
- package/src/channels/webhook.ts +199 -199
- package/src/channels/websocket.ts +87 -87
- package/src/channels/wechat.ts +149 -149
- package/src/core/a2a.ts +143 -143
- package/src/core/agent.ts +152 -152
- package/src/core/analytics-engine.ts +186 -186
- package/src/core/auth.ts +57 -57
- package/src/core/cache.ts +141 -141
- package/src/core/compose.ts +77 -77
- package/src/core/config.ts +14 -14
- package/src/core/errors.ts +148 -148
- package/src/core/hitl.ts +138 -138
- package/src/core/knowledge.ts +49 -4
- package/src/core/logger.ts +57 -57
- package/src/core/orchestrator.ts +215 -215
- package/src/core/performance.ts +187 -187
- package/src/core/rate-limiter.ts +128 -128
- package/src/core/room.ts +109 -109
- package/src/core/runtime.ts +152 -152
- package/src/core/sandbox.ts +101 -101
- package/src/core/security.ts +171 -171
- package/src/core/streaming.ts +195 -0
- package/src/core/types.ts +68 -68
- package/src/core/versioning.ts +106 -106
- package/src/core/watch.ts +178 -178
- package/src/core/workflow.ts +235 -235
- package/src/deploy/hermes.ts +156 -156
- package/src/deploy/openclaw.ts +200 -200
- package/src/dtv/data.ts +29 -29
- package/src/dtv/trust.ts +43 -43
- package/src/dtv/value.ts +47 -47
- package/src/i18n/index.ts +216 -216
- package/src/index.ts +6 -0
- package/src/marketplace/index.ts +223 -223
- package/src/memory/deepbrain.ts +108 -108
- package/src/memory/index.ts +34 -34
- package/src/plugins/index.ts +208 -208
- package/src/providers/index.ts +12 -3
- package/src/schema/oad.ts +155 -155
- package/src/skills/base.ts +16 -16
- package/src/skills/document.ts +100 -100
- package/src/skills/http.ts +35 -35
- package/src/skills/index.ts +27 -27
- package/src/skills/scheduler.ts +80 -80
- package/src/skills/webhook-trigger.ts +59 -59
- package/src/templates/code-reviewer.ts +34 -34
- package/src/templates/customer-service.ts +80 -80
- package/src/templates/data-analyst.ts +70 -70
- package/src/templates/executive-assistant.ts +71 -71
- package/src/templates/financial-advisor.ts +60 -60
- package/src/templates/knowledge-base.ts +31 -31
- package/src/templates/legal-assistant.ts +71 -71
- package/src/templates/sales-assistant.ts +79 -79
- package/src/templates/teacher.ts +79 -79
- package/src/testing/index.ts +181 -181
- package/src/tools/calculator.ts +73 -73
- package/src/tools/datetime.ts +149 -149
- package/src/tools/gateway.ts +220 -0
- package/src/tools/json-transform.ts +187 -187
- package/src/tools/mcp.ts +76 -76
- package/src/tools/text-analysis.ts +116 -116
- package/templates/Dockerfile +15 -15
- package/templates/code-reviewer/README.md +27 -27
- package/templates/code-reviewer/oad.yaml +41 -41
- package/templates/customer-service/README.md +22 -22
- package/templates/customer-service/oad.yaml +36 -36
- package/templates/docker-compose.yml +21 -21
- package/templates/ecommerce-assistant/README.md +45 -0
- package/templates/ecommerce-assistant/oad.yaml +47 -0
- package/templates/knowledge-base/README.md +28 -28
- package/templates/knowledge-base/oad.yaml +38 -38
- package/templates/sales-assistant/README.md +26 -26
- package/templates/sales-assistant/oad.yaml +43 -43
- package/templates/tech-support/README.md +43 -0
- package/templates/tech-support/oad.yaml +45 -0
- package/tests/a2a.test.ts +66 -66
- package/tests/agent.test.ts +72 -72
- package/tests/analytics.test.ts +50 -50
- package/tests/channel.test.ts +39 -39
- package/tests/e2e.test.ts +134 -134
- package/tests/errors.test.ts +83 -83
- package/tests/gateway.test.ts +71 -0
- package/tests/hitl.test.ts +71 -71
- package/tests/i18n.test.ts +41 -41
- package/tests/mcp.test.ts +54 -54
- package/tests/oad.test.ts +68 -68
- package/tests/performance.test.ts +115 -115
- package/tests/plugin.test.ts +74 -74
- package/tests/room.test.ts +106 -106
- package/tests/runtime.test.ts +42 -42
- package/tests/sandbox.test.ts +46 -46
- package/tests/security.test.ts +60 -60
- package/tests/streaming.test.ts +109 -0
- package/tests/templates.test.ts +77 -77
- package/tests/v070.test.ts +76 -76
- package/tests/versioning.test.ts +75 -75
- package/tests/voice.test.ts +61 -61
- package/tests/webhook.test.ts +29 -29
- package/tests/workflow.test.ts +143 -143
- package/tsconfig.json +19 -19
- package/vitest.config.ts +9 -9
package/src/channels/voice.ts
CHANGED
|
@@ -1,106 +1,106 @@
|
|
|
1
|
-
import { BaseChannel } from './index';
|
|
2
|
-
import type { Message } from '../core/types';
|
|
3
|
-
import { Logger } from '../core/logger';
|
|
4
|
-
|
|
5
|
-
// ── Voice Channel Types ─────────────────────────────────────
|
|
6
|
-
|
|
7
|
-
export interface STTProvider {
|
|
8
|
-
name: string;
|
|
9
|
-
transcribe(audio: Buffer, options?: STTOptions): Promise<string>;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface TTSProvider {
|
|
13
|
-
name: string;
|
|
14
|
-
synthesize(text: string, options?: TTSOptions): Promise<Buffer>;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface STTOptions {
|
|
18
|
-
language?: string;
|
|
19
|
-
model?: string;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface TTSOptions {
|
|
23
|
-
voice?: string;
|
|
24
|
-
speed?: number;
|
|
25
|
-
language?: string;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export interface VoiceChannelConfig {
|
|
29
|
-
sttProvider?: STTProvider;
|
|
30
|
-
ttsProvider?: TTSProvider;
|
|
31
|
-
sampleRate?: number;
|
|
32
|
-
language?: string;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// ── Voice Channel ───────────────────────────────────────────
|
|
36
|
-
|
|
37
|
-
export class VoiceChannel extends BaseChannel {
|
|
38
|
-
type = 'voice';
|
|
39
|
-
private config: VoiceChannelConfig;
|
|
40
|
-
private logger = new Logger('voice-channel');
|
|
41
|
-
private running = false;
|
|
42
|
-
|
|
43
|
-
constructor(config?: VoiceChannelConfig) {
|
|
44
|
-
super();
|
|
45
|
-
this.config = config ?? {};
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
async start(): Promise<void> {
|
|
49
|
-
this.running = true;
|
|
50
|
-
this.logger.info('Voice channel started', {
|
|
51
|
-
stt: this.config.sttProvider?.name ?? 'none',
|
|
52
|
-
tts: this.config.ttsProvider?.name ?? 'none',
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
async stop(): Promise<void> {
|
|
57
|
-
this.running = false;
|
|
58
|
-
this.logger.info('Voice channel stopped');
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
isRunning(): boolean {
|
|
62
|
-
return this.running;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/** Process audio input: STT → Agent → TTS */
|
|
66
|
-
async processAudio(audio: Buffer): Promise<{ text: string; response: string; audioResponse?: Buffer }> {
|
|
67
|
-
if (!this.handler) throw new Error('No message handler set');
|
|
68
|
-
|
|
69
|
-
// STT
|
|
70
|
-
let text: string;
|
|
71
|
-
if (this.config.sttProvider) {
|
|
72
|
-
text = await this.config.sttProvider.transcribe(audio, { language: this.config.language });
|
|
73
|
-
} else {
|
|
74
|
-
text = audio.toString('utf-8'); // Fallback: treat as text
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
this.logger.debug('STT result', { text });
|
|
78
|
-
|
|
79
|
-
// Create message and send to agent
|
|
80
|
-
const message: Message = {
|
|
81
|
-
id: `voice_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
|
|
82
|
-
role: 'user',
|
|
83
|
-
content: text,
|
|
84
|
-
timestamp: Date.now(),
|
|
85
|
-
metadata: { channel: 'voice' },
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
const response = await this.handler(message);
|
|
89
|
-
|
|
90
|
-
// TTS
|
|
91
|
-
let audioResponse: Buffer | undefined;
|
|
92
|
-
if (this.config.ttsProvider) {
|
|
93
|
-
audioResponse = await this.config.ttsProvider.synthesize(response.content, { language: this.config.language });
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return { text, response: response.content, audioResponse };
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
setSTTProvider(provider: STTProvider): void {
|
|
100
|
-
this.config.sttProvider = provider;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
setTTSProvider(provider: TTSProvider): void {
|
|
104
|
-
this.config.ttsProvider = provider;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
1
|
+
import { BaseChannel } from './index';
|
|
2
|
+
import type { Message } from '../core/types';
|
|
3
|
+
import { Logger } from '../core/logger';
|
|
4
|
+
|
|
5
|
+
// ── Voice Channel Types ─────────────────────────────────────
|
|
6
|
+
|
|
7
|
+
export interface STTProvider {
|
|
8
|
+
name: string;
|
|
9
|
+
transcribe(audio: Buffer, options?: STTOptions): Promise<string>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface TTSProvider {
|
|
13
|
+
name: string;
|
|
14
|
+
synthesize(text: string, options?: TTSOptions): Promise<Buffer>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface STTOptions {
|
|
18
|
+
language?: string;
|
|
19
|
+
model?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface TTSOptions {
|
|
23
|
+
voice?: string;
|
|
24
|
+
speed?: number;
|
|
25
|
+
language?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface VoiceChannelConfig {
|
|
29
|
+
sttProvider?: STTProvider;
|
|
30
|
+
ttsProvider?: TTSProvider;
|
|
31
|
+
sampleRate?: number;
|
|
32
|
+
language?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// ── Voice Channel ───────────────────────────────────────────
|
|
36
|
+
|
|
37
|
+
export class VoiceChannel extends BaseChannel {
|
|
38
|
+
type = 'voice';
|
|
39
|
+
private config: VoiceChannelConfig;
|
|
40
|
+
private logger = new Logger('voice-channel');
|
|
41
|
+
private running = false;
|
|
42
|
+
|
|
43
|
+
constructor(config?: VoiceChannelConfig) {
|
|
44
|
+
super();
|
|
45
|
+
this.config = config ?? {};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async start(): Promise<void> {
|
|
49
|
+
this.running = true;
|
|
50
|
+
this.logger.info('Voice channel started', {
|
|
51
|
+
stt: this.config.sttProvider?.name ?? 'none',
|
|
52
|
+
tts: this.config.ttsProvider?.name ?? 'none',
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async stop(): Promise<void> {
|
|
57
|
+
this.running = false;
|
|
58
|
+
this.logger.info('Voice channel stopped');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
isRunning(): boolean {
|
|
62
|
+
return this.running;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Process audio input: STT → Agent → TTS */
|
|
66
|
+
async processAudio(audio: Buffer): Promise<{ text: string; response: string; audioResponse?: Buffer }> {
|
|
67
|
+
if (!this.handler) throw new Error('No message handler set');
|
|
68
|
+
|
|
69
|
+
// STT
|
|
70
|
+
let text: string;
|
|
71
|
+
if (this.config.sttProvider) {
|
|
72
|
+
text = await this.config.sttProvider.transcribe(audio, { language: this.config.language });
|
|
73
|
+
} else {
|
|
74
|
+
text = audio.toString('utf-8'); // Fallback: treat as text
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
this.logger.debug('STT result', { text });
|
|
78
|
+
|
|
79
|
+
// Create message and send to agent
|
|
80
|
+
const message: Message = {
|
|
81
|
+
id: `voice_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
|
|
82
|
+
role: 'user',
|
|
83
|
+
content: text,
|
|
84
|
+
timestamp: Date.now(),
|
|
85
|
+
metadata: { channel: 'voice' },
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const response = await this.handler(message);
|
|
89
|
+
|
|
90
|
+
// TTS
|
|
91
|
+
let audioResponse: Buffer | undefined;
|
|
92
|
+
if (this.config.ttsProvider) {
|
|
93
|
+
audioResponse = await this.config.ttsProvider.synthesize(response.content, { language: this.config.language });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return { text, response: response.content, audioResponse };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
setSTTProvider(provider: STTProvider): void {
|
|
100
|
+
this.config.sttProvider = provider;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
setTTSProvider(provider: TTSProvider): void {
|
|
104
|
+
this.config.ttsProvider = provider;
|
|
105
|
+
}
|
|
106
|
+
}
|
package/src/channels/web.ts
CHANGED
|
@@ -26,14 +26,14 @@ const TEMPLATES_HTML = `<!DOCTYPE html>
|
|
|
26
26
|
<title>Agent Templates</title>
|
|
27
27
|
<style>
|
|
28
28
|
*{margin:0;padding:0;box-sizing:border-box}
|
|
29
|
-
body{background:#
|
|
29
|
+
body{background:#0f0f23;color:#e0e0e0;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;padding:24px}
|
|
30
30
|
h1{font-size:28px;margin-bottom:8px;color:#fff}
|
|
31
|
-
.sub{color:#
|
|
31
|
+
.sub{color:#8a8aa0;margin-bottom:32px;font-size:14px}
|
|
32
32
|
nav{margin-bottom:24px}
|
|
33
33
|
nav a{color:#818cf8;text-decoration:none;margin-right:16px;font-size:14px}
|
|
34
34
|
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:16px}
|
|
35
|
-
.card{background:#
|
|
36
|
-
.card:hover{border-color:#818cf8;transform:translateY(-2px)}
|
|
35
|
+
.card{background:#1a1a3a;border:1px solid #2d2d4e;border-radius:14px;padding:24px;cursor:pointer;transition:all .2s}
|
|
36
|
+
.card:hover{border-color:#818cf8;transform:translateY(-2px);box-shadow:0 4px 20px rgba(129,140,248,.15)}
|
|
37
37
|
.card .icon{font-size:32px;margin-bottom:12px}
|
|
38
38
|
.card h3{font-size:16px;color:#fff;margin-bottom:8px}
|
|
39
39
|
.card p{font-size:13px;color:#888;line-height:1.5}
|
|
@@ -65,7 +65,7 @@ const CHAT_HTML = `<!DOCTYPE html>
|
|
|
65
65
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
|
66
66
|
<title>OPC Agent</title>
|
|
67
67
|
<style>
|
|
68
|
-
:root{--bg:#
|
|
68
|
+
:root{--bg:#0f0f23;--surface:#1a1a3a;--border:#2d2d4e;--text:#e0e0e0;--text-dim:#8a8aa0;--accent:#818cf8;--accent-hover:#6366f1;--user-bg:#667eea;--user-hover:#5a6fd6;--error-bg:#7f1d1d;--error-text:#fca5a5;--success:#22c55e;--radius:14px}
|
|
69
69
|
*{margin:0;padding:0;box-sizing:border-box}
|
|
70
70
|
body{background:var(--bg);color:var(--text);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',sans-serif;height:100vh;height:100dvh;display:flex;flex-direction:column;overflow:hidden}
|
|
71
71
|
header{background:var(--surface);padding:14px 20px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:12px;flex-shrink:0;backdrop-filter:blur(12px)}
|
|
@@ -88,8 +88,8 @@ nav.header-nav a:hover{color:#fff;background:rgba(255,255,255,.06)}
|
|
|
88
88
|
.msg-wrap.user{align-items:flex-end}
|
|
89
89
|
.msg-wrap.assistant{align-items:flex-start}
|
|
90
90
|
.msg{max-width:min(720px,85%);padding:10px 14px;border-radius:var(--radius);line-height:1.7;font-size:14px;word-break:break-word;position:relative;transition:all .2s}
|
|
91
|
-
.msg.user{background:
|
|
92
|
-
.msg.assistant{background
|
|
91
|
+
.msg.user{background:linear-gradient(135deg,#667eea,#764ba2);color:#fff;border-bottom-right-radius:4px;box-shadow:0 2px 8px rgba(102,126,234,.35)}
|
|
92
|
+
.msg.assistant{background:#2a2a4a;color:var(--text);border:1px solid var(--border);border-bottom-left-radius:4px;box-shadow:0 2px 8px rgba(0,0,0,.25)}
|
|
93
93
|
.msg.error{background:var(--error-bg);color:var(--error-text);border:1px solid rgba(239,68,68,.3)}
|
|
94
94
|
.msg pre{background:rgba(0,0,0,.4);padding:12px;border-radius:8px;overflow-x:auto;margin:8px 0;font-size:13px;font-family:'JetBrains Mono','Fira Code','Cascadia Code',monospace;line-height:1.5}
|
|
95
95
|
.msg code{font-family:'JetBrains Mono','Fira Code','Cascadia Code',monospace;font-size:13px;background:rgba(0,0,0,.3);padding:1px 5px;border-radius:4px}
|
|
@@ -113,7 +113,7 @@ nav.header-nav a:hover{color:#fff;background:rgba(255,255,255,.06)}
|
|
|
113
113
|
#input{flex:1;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius);padding:10px 14px;color:#fff;font-size:14px;outline:none;resize:none;max-height:150px;min-height:42px;font-family:inherit;line-height:1.5;transition:border-color .2s}
|
|
114
114
|
#input:focus{border-color:var(--accent)}
|
|
115
115
|
#input::placeholder{color:var(--text-dim)}
|
|
116
|
-
#send{background:
|
|
116
|
+
#send{background:linear-gradient(135deg,#667eea,#764ba2);color:#fff;border:none;border-radius:var(--radius);padding:0 16px;height:42px;font-size:14px;font-weight:600;cursor:pointer;transition:all .2s;display:flex;align-items:center;justify-content:center;flex-shrink:0;letter-spacing:.5px}
|
|
117
117
|
#send:hover{background:var(--user-hover);transform:scale(1.05)}
|
|
118
118
|
#send:disabled{background:#334155;cursor:not-allowed;transform:none}
|
|
119
119
|
.empty-state{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;color:var(--text-dim);gap:12px;padding:40px;text-align:center}
|
|
@@ -132,15 +132,15 @@ nav.header-nav a:hover{color:#fff;background:rgba(255,255,255,.06)}
|
|
|
132
132
|
<body>
|
|
133
133
|
<header>
|
|
134
134
|
<div class="avatar" id="avatar">🤖</div>
|
|
135
|
-
<div class="info"><h1 id="title">OPC Agent</h1><div class="status"><span class="dot"></span
|
|
135
|
+
<div class="info"><h1 id="title">OPC Agent</h1><div class="status"><span class="dot"></span>在线</div></div>
|
|
136
136
|
<nav class="header-nav"><a href="/dashboard">Dashboard</a><a href="/templates">Templates</a></nav>
|
|
137
137
|
</header>
|
|
138
138
|
<div id="messages">
|
|
139
|
-
<div class="empty-state" id="empty"><div class="logo">💬</div><h2
|
|
139
|
+
<div class="empty-state" id="empty"><div class="logo">💬</div><h2>开始对话</h2><p>在下方输入消息与 AI 助手对话。</p></div>
|
|
140
140
|
</div>
|
|
141
141
|
<div id="input-area">
|
|
142
|
-
<textarea id="input" rows="1" placeholder="
|
|
143
|
-
<button id="send" aria-label="
|
|
142
|
+
<textarea id="input" rows="1" placeholder="输入消息…" autocomplete="off"></textarea>
|
|
143
|
+
<button id="send" aria-label="发送">发送</button>
|
|
144
144
|
</div>
|
|
145
145
|
<script>
|
|
146
146
|
const msgs=document.getElementById('messages'),input=document.getElementById('input'),btn=document.getElementById('send'),empty=document.getElementById('empty');
|
|
@@ -193,7 +193,7 @@ async function send(){
|
|
|
193
193
|
addMsg('user',text);
|
|
194
194
|
const wrap=document.createElement('div');wrap.className='msg-wrap assistant';
|
|
195
195
|
const d=document.createElement('div');d.className='msg assistant';
|
|
196
|
-
d.innerHTML='<div class="typing"><span></span><span></span><span></span></div>';
|
|
196
|
+
d.innerHTML='<div class="typing"><span></span><span></span><span></span><small style="margin-left:6px;font-size:12px;color:#8a8aa0">思考中…</small></div>';
|
|
197
197
|
wrap.appendChild(d);
|
|
198
198
|
const time=document.createElement('div');time.className='msg-time';time.textContent=fmtTime();
|
|
199
199
|
wrap.appendChild(time);
|
|
@@ -237,17 +237,17 @@ const DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
237
237
|
<title>OPC Dashboard</title>
|
|
238
238
|
<style>
|
|
239
239
|
*{margin:0;padding:0;box-sizing:border-box}
|
|
240
|
-
body{background:#
|
|
240
|
+
body{background:#0f0f23;color:#e0e0e0;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;padding:24px}
|
|
241
241
|
h1{font-size:24px;margin-bottom:24px;color:#fff}
|
|
242
242
|
.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:16px;margin-bottom:32px}
|
|
243
|
-
.card{background:#
|
|
244
|
-
.card .label{font-size:12px;color:#
|
|
243
|
+
.card{background:#1a1a3a;border:1px solid #2d2d4e;border-radius:14px;padding:20px}
|
|
244
|
+
.card .label{font-size:12px;color:#8a8aa0;text-transform:uppercase;letter-spacing:1px}
|
|
245
245
|
.card .value{font-size:32px;font-weight:700;color:#818cf8;margin-top:4px}
|
|
246
246
|
.card .sub{font-size:12px;color:#555;margin-top:4px}
|
|
247
247
|
nav{margin-bottom:24px}
|
|
248
248
|
nav a{color:#818cf8;text-decoration:none;margin-right:16px;font-size:14px}
|
|
249
249
|
nav a:hover{text-decoration:underline}
|
|
250
|
-
.chart{background:#
|
|
250
|
+
.chart{background:#1a1a3a;border:1px solid #2d2d4e;border-radius:14px;padding:20px;margin-bottom:16px}
|
|
251
251
|
.chart h3{font-size:14px;color:#888;margin-bottom:12px}
|
|
252
252
|
</style>
|
|
253
253
|
</head>
|