openclaw-openagent 1.0.12 → 1.0.14

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.
Files changed (132) hide show
  1. package/dist/index.js +2 -0
  2. package/dist/src/config/config-schema.d.ts +9 -0
  3. package/dist/src/config/config-schema.js +6 -0
  4. package/dist/src/messaging/executor.d.ts +2 -3
  5. package/dist/src/messaging/executor.js +4 -31
  6. package/dist/src/plugin-ui/assets/openagent-override.js +124 -18
  7. package/dist/src/proxy/auth-proxy.js +5 -0
  8. package/dist/src/runtime/update-checker.d.ts +18 -0
  9. package/dist/src/runtime/update-checker.js +216 -0
  10. package/index.ts +3 -0
  11. package/package.json +1 -3
  12. package/src/app/channel-tools.ts +0 -249
  13. package/src/app/discovery-tools.ts +0 -273
  14. package/src/app/download-file-tool.ts +0 -133
  15. package/src/app/hooks.ts +0 -144
  16. package/src/app/index.ts +0 -84
  17. package/src/app/messaging-tools.ts +0 -79
  18. package/src/app/ops-tools.ts +0 -155
  19. package/src/app/remote-agent-tool.ts +0 -762
  20. package/src/app/types.ts +0 -68
  21. package/src/app/upload-file-tool.ts +0 -411
  22. package/src/app/verbose-preflight.ts +0 -190
  23. package/src/auth/config.ts +0 -266
  24. package/src/auth/credential-manager.ts +0 -146
  25. package/src/auth/index.ts +0 -24
  26. package/src/auth/verify.ts +0 -99
  27. package/src/channel.ts +0 -566
  28. package/src/compat.ts +0 -94
  29. package/src/config/config-schema.ts +0 -39
  30. package/src/messaging/aggregator.ts +0 -120
  31. package/src/messaging/collector.ts +0 -89
  32. package/src/messaging/executor.ts +0 -72
  33. package/src/messaging/inbound.ts +0 -150
  34. package/src/messaging/index.ts +0 -11
  35. package/src/messaging/mention-protocol.ts +0 -94
  36. package/src/messaging/process-c2c-request.ts +0 -564
  37. package/src/messaging/process-message.ts +0 -373
  38. package/src/messaging/scheduler.ts +0 -55
  39. package/src/messaging/types.ts +0 -38
  40. package/src/plugin-ui/adapters/oc-2026-04.js +0 -103
  41. package/src/plugin-ui/adapters/oc-2026-05.js +0 -125
  42. package/src/plugin-ui/adapters/oc-2026-06.js +0 -125
  43. package/src/plugin-ui/adapters/oc-unknown.js +0 -48
  44. package/src/plugin-ui/assets/agentbook-icon.svg +0 -5
  45. package/src/plugin-ui/assets/bg.png +0 -0
  46. package/src/plugin-ui/assets/icon.png +0 -0
  47. package/src/plugin-ui/assets/magic.svg +0 -5
  48. package/src/plugin-ui/assets/openagent-override.js +0 -11156
  49. package/src/plugin-ui/build.cjs +0 -463
  50. package/src/plugin-ui/index.ts +0 -18
  51. package/src/plugin-ui/modules/agent-book/panel/agent-book.js +0 -570
  52. package/src/plugin-ui/modules/agent-book/panel/agent-card.js +0 -170
  53. package/src/plugin-ui/modules/agent-book/panel/agent-data.js +0 -643
  54. package/src/plugin-ui/modules/agent-book/panel/inject-ui.js +0 -665
  55. package/src/plugin-ui/modules/agent-book/panel/mention-state.js +0 -206
  56. package/src/plugin-ui/modules/agent-book/panel/styles.js +0 -833
  57. package/src/plugin-ui/modules/agent-book/panel/theme-adapter.js +0 -86
  58. package/src/plugin-ui/modules/agent-book/remote-agent-tool/components-core.js +0 -295
  59. package/src/plugin-ui/modules/agent-book/remote-agent-tool/thought-chain-card.js +0 -211
  60. package/src/plugin-ui/modules/agent-book/scanner.js +0 -131
  61. package/src/plugin-ui/modules/agent-book/travelcard/travel-cards.js +0 -500
  62. package/src/plugin-ui/modules/agent-book/travelcard/travel-engine.js +0 -652
  63. package/src/plugin-ui/modules/agent-book/travelcard/travel-styles.js +0 -256
  64. package/src/plugin-ui/modules/loader/bootstrap.js +0 -38
  65. package/src/plugin-ui/modules/loader/shared-state.js +0 -838
  66. package/src/plugin-ui/modules/loader/ws-intercept.js +0 -199
  67. package/src/plugin-ui/modules/remote-agent/chunk-parser.js +0 -161
  68. package/src/plugin-ui/modules/remote-agent/execution-card.js +0 -530
  69. package/src/plugin-ui/modules/remote-agent/markdown-renderer.js +0 -256
  70. package/src/plugin-ui/modules/remote-agent/media-links.js +0 -151
  71. package/src/plugin-ui/modules/remote-agent/native-style-adapter.js +0 -134
  72. package/src/plugin-ui/modules/remote-agent/output-card.js +0 -342
  73. package/src/plugin-ui/modules/remote-agent/progress-store.js +0 -363
  74. package/src/plugin-ui/modules/remote-agent/render-hooks.js +0 -1700
  75. package/src/plugin-ui/modules/remote-agent/styles.js +0 -896
  76. package/src/plugin-ui/modules/remote-agent/tool-card-model.js +0 -130
  77. package/src/plugin-ui/postinstall-deploy.cjs +0 -52
  78. package/src/plugin-ui/ui-extension-loader/backup.ts +0 -92
  79. package/src/plugin-ui/ui-extension-loader/index.ts +0 -276
  80. package/src/plugin-ui/ui-extension-loader/locator.ts +0 -152
  81. package/src/plugin-ui/ui-extension-loader/manifest.ts +0 -57
  82. package/src/plugin-ui/ui-extension-loader/registry-regex.ts +0 -944
  83. package/src/plugin-ui/ui-extension-loader/removed-extensions.ts +0 -70
  84. package/src/plugin-ui/ui-extension-loader/types.ts +0 -72
  85. package/src/proxy/auth-proxy.ts +0 -409
  86. package/src/runtime/account.ts +0 -549
  87. package/src/runtime/index.ts +0 -7
  88. package/src/runtime/plugin-runtime.ts +0 -94
  89. package/src/runtime/registry.ts +0 -71
  90. package/src/sdk/CLASS_MAP.md +0 -143
  91. package/src/sdk/index.d.ts +0 -126
  92. package/src/sdk/index.js +0 -23990
  93. package/src/sdk/modules/cloud-search-module.js +0 -1117
  94. package/src/sdk/modules/follow-module.js +0 -1069
  95. package/src/sdk/modules/group-module.js +0 -7397
  96. package/src/sdk/modules/relationship-module.js +0 -2269
  97. package/src/sdk/modules/signaling-module.js +0 -1468
  98. package/src/sdk/modules/tim-upload-plugin.js +0 -730
  99. package/src/sdk/node-env/http-request.js +0 -90
  100. package/src/sdk/node-env/index.js +0 -57
  101. package/src/sdk/node-env/storage.js +0 -114
  102. package/src/sdk/package.json +0 -10
  103. package/src/sdk/tsconfig.json +0 -16
  104. package/src/state/pending-invocation-store.ts +0 -43
  105. package/src/state/store.ts +0 -756
  106. package/src/tim/c2c.ts +0 -451
  107. package/src/tim/channels.ts +0 -364
  108. package/src/tim/client.ts +0 -330
  109. package/src/tim/index.ts +0 -18
  110. package/src/tim/messages.ts +0 -166
  111. package/src/tim/sdk-logger-init.ts +0 -50
  112. package/src/tools.ts +0 -10
  113. package/src/transport/factory.ts +0 -95
  114. package/src/transport/oasn/index.ts +0 -17
  115. package/src/transport/oasn/oasn-agent-card.ts +0 -111
  116. package/src/transport/oasn/oasn-discovery.ts +0 -108
  117. package/src/transport/oasn/oasn-files.ts +0 -210
  118. package/src/transport/oasn/oasn-http.ts +0 -529
  119. package/src/transport/oasn/oasn-invocation.ts +0 -604
  120. package/src/transport/oasn/oasn-normalize.ts +0 -133
  121. package/src/transport/oasn/oasn-register.ts +0 -33
  122. package/src/transport/oasn/oasn-transport.ts +0 -318
  123. package/src/transport/oasn/oasn-types.ts +0 -404
  124. package/src/transport/tim/index.ts +0 -8
  125. package/src/transport/tim/tim-transport.ts +0 -515
  126. package/src/transport/types.ts +0 -538
  127. package/src/types/openclaw-plugin-sdk-media-store.d.ts +0 -9
  128. package/src/types/openclaw.d.ts +0 -97
  129. package/src/types/tencentcloud-chat.d.ts +0 -15
  130. package/src/util/http.ts +0 -113
  131. package/src/util/logger.ts +0 -131
  132. package/src/util/url-resolver.ts +0 -77
@@ -1,131 +0,0 @@
1
- // ── OpenAgent DOM Scanner — lightweight non-ToolCard DOM helpers ──
2
- //
3
- // RenderHook is the only supported rendering path for remote-agent ToolCards.
4
- // This scanner must not decorate, restore, or replace call_remote_agent cards.
5
-
6
- // CL.clScanCount lives on CL.clScanCount
7
-
8
- function _clIsInsideNativeToolHost(node) {
9
- var adapter = CL && CL.hostAdapter;
10
- var cur = node;
11
- var hops = 0;
12
- while (cur && cur !== document.body && hops < 12) {
13
- if (adapter && adapter.isToolLikeShell && adapter.isToolLikeShell(cur)) return true;
14
- cur = cur.parentElement;
15
- hops++;
16
- }
17
- return !!(node && node.closest && node.closest('details'));
18
- }
19
-
20
- function _clScan() {
21
- CL.clScanCount++;
22
-
23
- // ── 0.5 清理标记 → 显示为 @agentName ──
24
- // 新格式: {openagent_call_remote_agent:agent_id="...",agent_name="...",agent_bio="...",instruction="..."}
25
- // 旧格式: {openagent:agent_id="...",agent_name="..."}
26
- // 两种都要匹配清理;这里只处理普通聊天气泡,不处理 ToolCard。
27
- var CL_MENTION_STYLE = '';
28
- document.querySelectorAll('.chat-bubble').forEach(function(el) {
29
- if (el.dataset.clMarkerCleaned) return;
30
- if (_clIsInsideNativeToolHost(el)) return;
31
-
32
- var text = el.textContent || '';
33
- var rawRegex = /\{openagent(?:_call_remote_agent)?:agent_id="([^"]*)",agent_name="([^"]*)"(?:,[^{}]*)?\}\s*/g;
34
- if (!rawRegex.test(text)) return;
35
-
36
- if (el.querySelector('.cl-remote-agent-card')) {
37
- el.dataset.clMarkerCleaned = '1';
38
- return; // 包含活 DOM,跳过 innerHTML 重写
39
- }
40
-
41
- var htmlRegex = /\{openagent(?:_call_remote_agent)?:agent_id=(?:&quot;|")([^&"]*)(?:&quot;|"),agent_name=(?:&quot;|")([^&"]*)(?:&quot;|")(?:,[^{}]*)?\}\s*/g;
42
- if (CL && typeof CL.diagHtmlRewrite === 'function') {
43
- CL.diagHtmlRewrite('scanner.marker.before', el, { scan: CL.clScanCount });
44
- }
45
- el.innerHTML = el.innerHTML.replace(htmlRegex, function(_, agentId, agentName) {
46
- var safeAgentId = escHtml(agentId);
47
- var safeAgentName = escHtml(agentName);
48
- return '<code style="' + CL_MENTION_STYLE + '" data-agent-id="' + safeAgentId + '" data-agent-name="' + safeAgentName + '">@' + safeAgentName + '</code> ';
49
- });
50
- if (CL && typeof CL.diagHtmlRewrite === 'function') {
51
- CL.diagHtmlRewrite('scanner.marker.after', el, { scan: CL.clScanCount });
52
- }
53
- el.dataset.clMarkerCleaned = '1';
54
- });
55
-
56
- // ── 0.6 LLM 回复中的 agent 名称也加底色标签 ──
57
- var mentionedAgents = [];
58
- document.querySelectorAll('code').forEach(function(c) {
59
- var t = (c.textContent || '').trim();
60
- if (t.startsWith('@') && t.length > 1) {
61
- var name = t.substring(1);
62
- if (mentionedAgents.indexOf(name) === -1) mentionedAgents.push(name);
63
- }
64
- });
65
-
66
- if (CL.agentCache.length > 0) {
67
- for (var ai = 0; ai < CL.agentCache.length; ai++) {
68
- var n = CL.agentCache[ai].name;
69
- if (n && n.length >= 2 && mentionedAgents.indexOf(n) === -1) mentionedAgents.push(n);
70
- }
71
- }
72
-
73
- if (mentionedAgents.length > 0) {
74
- document.querySelectorAll('.chat-bubble').forEach(function(el) {
75
- if (el.dataset.clAgentStyled) return;
76
- if (_clIsInsideNativeToolHost(el)) return;
77
-
78
- var text = el.textContent || '';
79
- var html = el.innerHTML;
80
- var changed = false;
81
- for (var mi = 0; mi < mentionedAgents.length; mi++) {
82
- var aName = mentionedAgents[mi];
83
- if (html.includes('>@' + aName + '</code>')) continue;
84
- if (!text.includes(aName)) continue;
85
-
86
- var escaped = aName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
87
- var quoteWrap = new RegExp(
88
- '(?:[\u201C\u201D"\u300C]|&quot;)' + escaped + '(?:[\u201C\u201D"\u300D]|&quot;)',
89
- 'g'
90
- );
91
- var newHtml = html.replace(quoteWrap, '<code>@' + aName + '</code>');
92
- if (newHtml !== html) {
93
- html = newHtml;
94
- changed = true;
95
- }
96
- }
97
- // 包含活 DOM 时不重写 innerHTML
98
- if (el.querySelector('.cl-remote-agent-card')) {
99
- el.dataset.clAgentStyled = '1';
100
- return;
101
- }
102
- if (changed && CL && typeof CL.diagHtmlRewrite === 'function') {
103
- CL.diagHtmlRewrite('scanner.agent-style.before', el, {
104
- scan: CL.clScanCount,
105
- agents: mentionedAgents
106
- });
107
- }
108
- if (changed) el.innerHTML = html;
109
- if (changed && CL && typeof CL.diagHtmlRewrite === 'function') {
110
- CL.diagHtmlRewrite('scanner.agent-style.after', el, {
111
- scan: CL.clScanCount,
112
- agents: mentionedAgents
113
- });
114
- }
115
- el.dataset.clAgentStyled = '1';
116
- });
117
- }
118
-
119
- // Remote-agent ToolCards intentionally have no scanner fallback.
120
- // call/result cards, thought steps, completion state, and Agent pill rendering
121
- // must all come from remote-agent/render-hooks.js.
122
-
123
- // Travel card tags (<tr_card>, <sh_card>, etc.) are processed exclusively
124
- // by the RenderHook pipeline via _clScheduleOutputCardProcessing in output-card.js.
125
- // No scanner polling needed.
126
-
127
- // Continue scanning for 2 minutes (mention cleanup only).
128
- if (CL.clScanCount < 60) {
129
- setTimeout(_clScan, 2000);
130
- }
131
- }
@@ -1,500 +0,0 @@
1
- console.log('[openagent] ✅ Trip card CSS injected');
2
-
3
- // ── 2. 辅助函数 ──
4
-
5
- // _el / _text 已提升到文件顶部工具函数区(全局共用)
6
-
7
- function _formatTime(s) {
8
- if (!s) return '';
9
- const p = s.split(' ');
10
- return p.length >= 2 ? p[1].slice(0, 5) : '';
11
- }
12
-
13
- // Traffic icon unicode map
14
- const _TRAFFIC_ICONS = {
15
- FLIGHT: String.fromCodePoint(0xF11C9),
16
- TRAIN: String.fromCodePoint(0xF11CA),
17
- DRIVE: String.fromCodePoint(0xF1BBE),
18
- BUS: String.fromCodePoint(0xF1BBD),
19
- };
20
-
21
- // ── 3. convertTrafficPlanInfo (translated from commons.js _m.OC) ──
22
-
23
- function _convertTPI(tpi) {
24
- if (!tpi) return null;
25
- const summary = tpi.summary || {};
26
- const dep = tpi.departure || {};
27
- const arr = tpi.arrival || {};
28
- const detailList = tpi.detailList || [];
29
- const mainDetail = detailList.find(d => d.type && d.type !== 'DRIVE') || detailList[0];
30
- const getStation = ep => ep?.trafficHub?.name || '';
31
- const getDistrict = ep => ep?.trafficHub?.districtInfo?.districtName || '';
32
- return {
33
- startStationName: getStation(dep),
34
- endStationName: getStation(arr),
35
- startDistrictName: getDistrict(dep),
36
- endDistrictName: getDistrict(arr),
37
- startCode: dep?.trafficHub?.terminalCode,
38
- endCode: arr?.trafficHub?.terminalCode,
39
- startId: dep?.trafficHub?.districtInfo?.districtId,
40
- endId: arr?.trafficHub?.districtInfo?.districtId,
41
- travelTime: summary.transportTime,
42
- startDateTime: summary.startDateTime,
43
- endDateTime: summary.endDateTime,
44
- endDateTimeSuperscript: summary.endDateTimeSuperscript,
45
- startDateTimeFormatted: summary.startDateTimeFormatted,
46
- shiftNumber: mainDetail?.basicInfo?.transportNumber,
47
- nearbyText: summary.description,
48
- isRecommended: summary.isRecommended,
49
- isTransfer: summary.isTransfer,
50
- trafficType: mainDetail?.type,
51
- transferCount: summary.transferCount || 0,
52
- description: mainDetail?.basicInfo?.description,
53
- tagList: summary.tagList,
54
- };
55
- }
56
-
57
- // ── 4. Card Renderers ──
58
-
59
- /** TransportCard — 1:1 from TransportCard.jsx */
60
- function _renderTransportCard(uc) {
61
- if (!uc || uc.type !== 'transportCard') return null;
62
- const units = uc.unitList || [];
63
- if (units.length === 0) return null;
64
- const unit = units[0];
65
- if (!unit.trafficPlanInfo) return null;
66
- const X = _convertTPI(unit.trafficPlanInfo);
67
- if (!X) return null;
68
-
69
- const url = unit.trafficPlanInfo.summary?.url;
70
-
71
- const card = _el('div', 'trip-transport-card');
72
- if (url?.type === 'NORMAL_URL' && url.url) {
73
- card.style.cursor = 'pointer';
74
- card.onclick = () => window.open(url.url, '_blank');
75
- }
76
-
77
- const inner = _el('div', 'trip-tc-inner');
78
- card.appendChild(inner);
79
-
80
- // StationAndTime
81
- const w = X.startDateTime ? _formatTime(X.startDateTime) : '';
82
- const b = X.endDateTime ? _formatTime(X.endDateTime) : '';
83
- let H = w, z = b;
84
- const U = !!(w && b);
85
- if (!U) { H = X.startDistrictName; z = X.endDistrictName; }
86
- const Y = !!(H && z);
87
- if (!Y) { H = X.startStationName; z = X.endStationName; }
88
-
89
- const stationRow = _el('div', 'trip-tc-station-row');
90
- inner.appendChild(stationRow);
91
-
92
- // Left side
93
- const left = _el('div', 'trip-tc-left');
94
- stationRow.appendChild(left);
95
-
96
- const leftTop = _el('div', 'trip-tc-left-top');
97
- left.appendChild(leftTop);
98
-
99
- // Start time/city
100
- leftTop.appendChild(_text('span', 'trip-tc-time', H));
101
-
102
- // Arrow area
103
- const arrowArea = _el('div', 'trip-tc-arrow-area');
104
- leftTop.appendChild(arrowArea);
105
-
106
- // SVG arrow
107
- const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
108
- svg.setAttribute('width', '100%');
109
- svg.setAttribute('height', '20');
110
- svg.setAttribute('viewBox', '0 0 100 20');
111
- svg.setAttribute('preserveAspectRatio', 'none');
112
- svg.style.display = 'block';
113
- svg.style.flex = '1';
114
- svg.style.minWidth = '40px';
115
- const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
116
- line.setAttribute('x1', '0'); line.setAttribute('y1', '10');
117
- line.setAttribute('x2', '90'); line.setAttribute('y2', '10');
118
- line.setAttribute('stroke', '#C9CDD4'); line.setAttribute('stroke-width', '1');
119
- svg.appendChild(line);
120
- const polygon = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');
121
- polygon.setAttribute('points', '90,6 98,10 90,14');
122
- polygon.setAttribute('fill', '#C9CDD4');
123
- svg.appendChild(polygon);
124
- arrowArea.appendChild(svg);
125
-
126
- // Duration label
127
- const dur = _el('div', 'trip-tc-duration');
128
- if (X.travelTime) dur.appendChild(_text('span', '', X.travelTime));
129
- if (X.transferCount > 0) dur.appendChild(_text('span', '', ` ${X.transferCount}次中转`));
130
- arrowArea.appendChild(dur);
131
-
132
- // Start station name
133
- const startCode = (X.startId === X.startCode) ? undefined : X.startCode;
134
- if (Y) {
135
- left.appendChild(_text('span', 'trip-tc-station',
136
- X.startStationName + (startCode ? ` ${startCode}` : '')));
137
- }
138
-
139
- // Right side
140
- const right = _el('div', 'trip-tc-right');
141
- stationRow.appendChild(right);
142
-
143
- const endTime = _text('span', 'trip-tc-time', z);
144
- right.appendChild(endTime);
145
- if (X.endDateTimeSuperscript) {
146
- endTime.appendChild(_text('span', 'trip-tc-superscript', X.endDateTimeSuperscript));
147
- }
148
-
149
- const endCode = (X.endId === X.endCode) ? undefined : X.endCode;
150
- if (Y) {
151
- right.appendChild(_text('span', 'trip-tc-station',
152
- X.endStationName + (endCode ? ` ${endCode}` : '')));
153
- }
154
-
155
- // DescBox
156
- const desc = _el('div', 'trip-tc-desc');
157
- inner.appendChild(desc);
158
- const descParts = [];
159
-
160
- // Recommend tag
161
- const recTag = (X.tagList || []).find(t => t.tagType === 'recommend' || t.tagType === 'book' || t.tagType === 'TRAFFIC_PLAN');
162
- if (recTag?.tagValue) {
163
- descParts.push(_text('span', 'trip-tc-desc-tag trip-tc-desc-tag-positive', recTag.tagValue));
164
- }
165
-
166
- // Flight number — 始终显示"参考班次:"前缀
167
- if (!X.isTransfer && X.shiftNumber) {
168
- descParts.push(_text('span', 'trip-tc-desc-tag', '参考班次:' + X.shiftNumber));
169
- }
170
-
171
- // Date — 不显示日期(参考图一)
172
-
173
- // Join with dividers
174
- descParts.forEach((part, i) => {
175
- if (i > 0) desc.appendChild(_text('span', 'trip-tc-desc-divider', '|'));
176
- desc.appendChild(part);
177
- });
178
-
179
- return card;
180
- }
181
-
182
- /** ImageCard — POI 图片横向滚动 */
183
- function _renderImageCard(uc) {
184
- if (!uc || uc.type !== 'imageCard') return null;
185
- const units = (uc.unitList || []).filter(u => u.unitType === 'poi' && u.poiInfo);
186
- if (units.length === 0) return null;
187
-
188
- const container = _el('div', 'trip-img-card');
189
-
190
- units.forEach(u => {
191
- const poi = u.poiInfo;
192
- const imgs = poi.imgs || [];
193
- const name = poi.name || poi.poiName || '';
194
- const score = poi.hotScore;
195
- const desc = poi.description
196
- || poi.summary
197
- || poi.basicInfo?.description
198
- || poi.basicInfo?.summary
199
- || poi.rankName
200
- || '';
201
-
202
- // Only create items for POIs with images
203
- imgs.slice(0, 3).forEach(imgUrl => {
204
- const item = _el('div', 'trip-img-item');
205
-
206
- const img = document.createElement('img');
207
- img.src = imgUrl;
208
- img.alt = name;
209
- img.loading = 'lazy';
210
- item.appendChild(img);
211
-
212
- // Overlay with name
213
- const overlay = _el('div', 'trip-img-overlay');
214
- overlay.appendChild(_text('span', 'trip-img-name', name));
215
- item.appendChild(overlay);
216
-
217
- // Score badge
218
- if (score) {
219
- const scoreBadge = _el('div', 'trip-img-score');
220
- scoreBadge.appendChild(_text('span', 'trip-img-score-icon', '🔥'));
221
- scoreBadge.appendChild(_text('span', 'trip-img-score-text', String(score)));
222
- item.appendChild(scoreBadge);
223
- }
224
-
225
- container.appendChild(item);
226
- });
227
-
228
- // If only markdown fallback data is available, still render the useful text
229
- // instead of collapsing to an empty-looking image placeholder.
230
- if (imgs.length === 0 && name) {
231
- const item = _el('div', 'trip-img-item trip-img-fallback-item');
232
- item.appendChild(_text('span', 'trip-img-fallback-title', name));
233
- if (desc) {
234
- item.appendChild(_text('span', 'trip-img-fallback-desc', desc));
235
- }
236
- if (score) {
237
- item.appendChild(_text('span', 'trip-img-fallback-score', String(score)));
238
- }
239
- container.appendChild(item);
240
- }
241
- });
242
-
243
- return container.children.length > 0 ? container : null;
244
- }
245
-
246
- /** TimeDataCard — Day 分隔符 */
247
- function _renderTimeDataCard(content) {
248
- // 蓝色第N天 badge 已移除,参考图二不需要此组件
249
- return null;
250
- }
251
-
252
- /** ShoppingCard — 线路概览表格 */
253
- function _renderShoppingCard(dayList) {
254
- if (!dayList || dayList.length === 0) return null;
255
-
256
- const card = _el('div', 'trip-sh-card');
257
- card.appendChild(_text('span', 'trip-sh-title', '线路概览'));
258
-
259
- const table = _el('div', 'trip-sh-table');
260
- card.appendChild(table);
261
-
262
- dayList.forEach((day, idx) => {
263
- const row = _el('div', 'trip-sh-row');
264
- table.appendChild(row);
265
-
266
- // Day cell
267
- const dayCell = _el('div', 'trip-sh-day-cell');
268
- dayCell.appendChild(_text('span', 'trip-sh-day-title', 'DAY'));
269
- dayCell.appendChild(_text('span', 'trip-sh-day-num', String(idx + 1).padStart(2, '0')));
270
- row.appendChild(dayCell);
271
-
272
- // Detail cell
273
- const detail = _el('div', 'trip-sh-detail');
274
- row.appendChild(detail);
275
-
276
- const wrapper = _el('div', '');
277
- detail.appendChild(wrapper);
278
-
279
- // Districts
280
- const districts = day.districtInfos || [];
281
- if (districts.length > 0) {
282
- const dv = _el('div', 'trip-sh-districts');
283
- wrapper.appendChild(dv);
284
- districts.forEach((d, di) => {
285
- if (di > 0) dv.appendChild(_text('span', 'trip-sh-district-arrow', '→'));
286
- dv.appendChild(_text('span', 'trip-sh-district', d.districtName || ''));
287
- });
288
- }
289
-
290
- // POIs
291
- const units = day.itineraryUnitList || [];
292
- const poiNames = units.map(u => u.poiInfo?.basicInfo?.poiName || u.poiInfo?.name || '').filter(Boolean);
293
- if (poiNames.length > 0) {
294
- const pv = _el('div', 'trip-sh-pois');
295
- wrapper.appendChild(pv);
296
- poiNames.forEach((name, pi) => {
297
- if (pi > 0) pv.appendChild(_text('span', 'trip-sh-poi-arrow', '→'));
298
- pv.appendChild(_text('span', 'trip-sh-poi', name));
299
- });
300
- }
301
- });
302
-
303
- return card;
304
- }
305
-
306
- /** HotelAreaCard */
307
- function _renderHotelCard(uc) {
308
- if (!uc || uc.type !== 'hotelAreaCard') return null;
309
- const unit = (uc.unitList || [])[0];
310
- const areaList = unit?.hotelAreaList || [];
311
- const area = unit?.hotelArea || areaList.find(a => a && a.recommendArea) || areaList[0];
312
- if (!area) return null;
313
-
314
- const card = _el('div', 'trip-hotel-card');
315
- const header = _el('div', 'trip-hotel-header');
316
- card.appendChild(header);
317
-
318
- // Hotel icon (using emoji as fallback for icon font)
319
- header.appendChild(_text('span', 'trip-hotel-icon', String.fromCodePoint(0xF178F)));
320
- header.appendChild(_text('span', 'trip-hotel-title', `推荐住在 · ${area.name || ''}`));
321
- const desc = area.description
322
- || area.recommendation
323
- || (area.chosenRate ? `${Math.round(Number(area.chosenRate) * 100)}%的游客选择住在这里` : '');
324
- if (desc) {
325
- card.appendChild(_text('div', 'trip-hotel-desc', desc));
326
- }
327
-
328
- return card;
329
- }
330
-
331
- /** FoodCard */
332
- function _renderFoodCard(foodData) {
333
- if (!foodData) return null;
334
- const mt = foodData.mustTasteResult || foodData;
335
- const foods = (mt.hotFoodInfos || []).slice(0, 4);
336
- const rests = (mt.hotRestInfos || []).filter(r => r.name || r.restName).slice(0, 3);
337
- if (foods.length === 0 && rests.length === 0) return null;
338
-
339
- const card = _el('div', '', { margin: '16px 0' });
340
-
341
- // 标题
342
- var title = document.createElement('h3');
343
- title.textContent = '美食推荐';
344
- title.style.cssText = 'font-size:18px;font-weight:600;color:#111;margin:0 0 8px 0;line-height:26px;';
345
- card.appendChild(title);
346
-
347
- // 介绍段落:"当地特色美食有寿司、鳗鱼饭、天妇罗等,推荐口碑榜餐厅作为参考:"
348
- if (foods.length > 0) {
349
- var foodNames = foods.map(function(f) { return f.name || ''; }).filter(Boolean).join('、');
350
- var intro = document.createElement('p');
351
- intro.textContent = '当地特色美食有' + foodNames + '等,推荐口碑榜餐厅作为参考:';
352
- intro.style.cssText = 'font-size:15px;color:#333;line-height:1.7;margin:0 0 12px 0;';
353
- card.appendChild(intro);
354
- }
355
-
356
- // 餐厅列表(bullet list 样式,匹配图一)
357
- if (rests.length > 0) {
358
- var ul = document.createElement('ul');
359
- ul.style.cssText = 'list-style:disc;padding-left:20px;margin:0;';
360
- rests.forEach(function(r) {
361
- var li = document.createElement('li');
362
- li.style.cssText = 'margin-bottom:10px;font-size:15px;line-height:1.7;color:#333;';
363
-
364
- var nameEl = document.createElement('strong');
365
- nameEl.textContent = r.name || r.restName || '';
366
- li.appendChild(nameEl);
367
-
368
- if (r.rankTitle) {
369
- var desc = document.createTextNode(':' + r.rankTitle);
370
- li.appendChild(desc);
371
- }
372
- ul.appendChild(li);
373
- });
374
- card.appendChild(ul);
375
- }
376
-
377
- return card;
378
- }
379
-
380
- /** BudgetCard */
381
- function _renderBudgetCard(budgetData) {
382
- if (!budgetData) return null;
383
- const result = budgetData.result || budgetData;
384
- const budget = result.budget || result.budgetData || result;
385
- if (!budget) return null;
386
-
387
- function _readBudgetNumber(obj, fields) {
388
- for (var i = 0; i < fields.length; i++) {
389
- var value = obj && obj[fields[i]];
390
- if (typeof value === 'number' && Number.isFinite(value)) return value;
391
- if (typeof value === 'string') {
392
- var parsed = Number(value.replace(/[^\d.-]/g, ''));
393
- if (Number.isFinite(parsed)) return parsed;
394
- }
395
- }
396
- return 0;
397
- }
398
-
399
- function _readBudgetText(obj, fields) {
400
- for (var i = 0; i < fields.length; i++) {
401
- var value = obj && obj[fields[i]];
402
- if (typeof value === 'string' && value.trim()) return value.trim();
403
- }
404
- return '';
405
- }
406
-
407
- const totalPrice = _readBudgetNumber(budget, [
408
- 'totalPrice', 'totalAmount', 'totalCost', 'total', 'amount', 'price', 'cost', 'value'
409
- ]);
410
- const totalFormatted = _readBudgetText(budget, [
411
- 'totalPriceFormatted', 'totalAmountFormatted', 'totalCostFormatted',
412
- 'totalFormatted', 'amountFormatted', 'priceFormatted', 'formattedPrice', 'formatted'
413
- ]);
414
-
415
- const rawParts = budget.budgetParts || budget.parts || budget.items || budget.categories || budget.detailList || [];
416
- const description = budget.description || budget.desc || budget.summary || budget.remark || '';
417
-
418
- if (!totalPrice && !totalFormatted && (!rawParts || rawParts.length === 0) && !description) return null;
419
-
420
- const PART_COLORS = {
421
- TRAFFIC: { color: '#7585F8', bg: 'rgba(117,133,248,0.08)', title: '交通', icon: '✈️' },
422
- TICKETS: { color: '#1FC496', bg: 'rgba(31,196,150,0.08)', title: '景点', icon: '🎫' },
423
- STAY: { color: '#3290FA', bg: 'rgba(50,144,250,0.08)', title: '住宿', icon: '🏨' },
424
- };
425
-
426
- const card = _el('div', 'trip-budget-card');
427
- card.appendChild(_text('div', 'trip-budget-title', '费用预估'));
428
- if (description) {
429
- // 描述用 bullet list 格式(匹配图一)
430
- const descUl = document.createElement('ul');
431
- descUl.style.cssText = 'list-style:disc;padding-left:20px;margin:6px 0 0 0;';
432
- const descLi = document.createElement('li');
433
- descLi.className = 'trip-budget-desc';
434
- descLi.textContent = description;
435
- descUl.appendChild(descLi);
436
- card.appendChild(descUl);
437
- }
438
-
439
- const chart = _el('div', 'trip-budget-chart');
440
- card.appendChild(chart);
441
-
442
- // Header
443
- const header = _el('div', 'trip-budget-header');
444
- chart.appendChild(header);
445
- const person = (budget.person || 1) > 1 ? `${budget.person}人总价` : '1人总价';
446
- header.appendChild(_text('span', 'trip-budget-person', person));
447
- header.appendChild(_text('span', 'trip-budget-total',
448
- totalFormatted || (totalPrice ? `≈ ¥${totalPrice.toLocaleString()}` : '费用待估算')));
449
-
450
- // Parts
451
- const parts = (rawParts || []).map(p => {
452
- const type = p.type || p.category || p.key || p.name || p.title || 'OTHER';
453
- const price = _readBudgetNumber(p, [
454
- 'totalPrice', 'totalAmount', 'totalCost', 'total', 'amount', 'price', 'cost', 'value'
455
- ]);
456
- const formatted = _readBudgetText(p, [
457
- 'totalPriceFormatted', 'totalAmountFormatted', 'totalCostFormatted',
458
- 'totalFormatted', 'amountFormatted', 'priceFormatted', 'formattedPrice', 'formatted'
459
- ]);
460
- return {
461
- raw: p,
462
- type: String(type || 'OTHER').toUpperCase(),
463
- label: p.title || p.name || p.label || p.categoryName || '',
464
- totalPrice: price,
465
- totalPriceFormatted: formatted,
466
- };
467
- }).sort((a, b) => {
468
- const order = { TRAFFIC: 0, STAY: 1, TICKETS: 2 };
469
- return (order[a.type] ?? 99) - (order[b.type] ?? 99);
470
- });
471
-
472
- if (parts.length > 0) {
473
- const partsDiv = _el('div', 'trip-budget-parts');
474
- chart.appendChild(partsDiv);
475
-
476
- parts.forEach(p => {
477
- const cfg = PART_COLORS[p.type] || { color: '#ccc', bg: '#f5f5f5', title: p.label || p.type, icon: '📦' };
478
- const ratio = totalPrice > 0 ? (p.totalPrice || 0) / totalPrice : 0;
479
-
480
- const part = _el('div', 'trip-budget-part');
481
- partsDiv.appendChild(part);
482
-
483
- part.appendChild(_text('span', 'trip-budget-part-icon', cfg.icon));
484
- part.appendChild(_text('span', 'trip-budget-part-name', cfg.title));
485
-
486
- const bar = _el('div', 'trip-budget-bar', { backgroundColor: cfg.bg });
487
- const fill = _el('div', 'trip-budget-bar-fill', {
488
- width: `${Math.max(100 * ratio, 4)}%`,
489
- backgroundColor: cfg.color,
490
- });
491
- bar.appendChild(fill);
492
- part.appendChild(bar);
493
-
494
- part.appendChild(_text('span', 'trip-budget-part-price',
495
- p.totalPriceFormatted || (p.totalPrice ? `¥${p.totalPrice.toLocaleString()}` : '待估算')));
496
- });
497
- }
498
-
499
- return card;
500
- }