diff --git a/cmd/ohos/HomeAgent/entry/src/main/ets/common/BridgeCaps.ets b/cmd/ohos/HomeAgent/entry/src/main/ets/common/BridgeCaps.ets
index 419074a..f3d0a22 100644
--- a/cmd/ohos/HomeAgent/entry/src/main/ets/common/BridgeCaps.ets
+++ b/cmd/ohos/HomeAgent/entry/src/main/ets/common/BridgeCaps.ets
@@ -426,98 +426,3 @@ export function capDeviceInfo(deviceId: string, deviceName: string): CapResult {
};
return okResult(JSON.stringify(payload));
}
-
-// ===== screensue 内容解析 =====
-// 服务端协议: screensue [秒] <内容>;0=常驻。
-
-export interface ScreensuePayload {
- duration: number; // 秒;0 表示常驻直到用户关闭
- content: string;
-}
-
-export function parseScreensue(rawArgs: string): ScreensuePayload {
- const p: ScreensuePayload = { duration: 5, content: '' };
- const leadingSpaces: RegExp = new RegExp('^\\s+');
- const firstSpace: RegExp = new RegExp('\\s');
- let rest: string = rawArgs.replace(leadingSpaces, '');
- const splitAt: number = rest.search(firstSpace);
- if (splitAt > 0) {
- const first: string = rest.substring(0, splitAt);
- const digits: RegExp = new RegExp('^\\d+$');
- if (digits.test(first)) {
- p.duration = Math.min(parseInt(first, 10), 86400);
- rest = rest.substring(splitAt).replace(leadingSpaces, '');
- }
- }
- p.content = rest;
- return p;
-}
-
-/**
- * 判断 agent 下发的 screensue 内容是不是 HTML 片段。
- *
- * 服务端协议两侧都允许 HTML(localuse 的 local_screensue 在 Linux 用 browsh/w3m
- * 渲染 HTML;remotedevice 的工具说明写的就是「显示内容/HTML」)。此前鸿蒙端一律
- * 塞进 Text(),于是 HTML 被当成普通字符串原样显示成标签。
- *
- * 判据取「首个非空字符是 '<'」并且「存在配对的 '>'」且含字母/斜杠,
- * 避免把 "<3" 这类纯文本误判成标签;再排除纯文本里常见的比较式(如 "a < b")。
- */
-export function looksLikeHtml(content: string): boolean {
- const trimmed: string = content.trim();
- if (trimmed.length === 0 || trimmed.charAt(0) !== '<') {
- return false;
- }
- const close: number = trimmed.indexOf('>');
- if (close < 0) {
- return false;
- }
- // 标签名必须是字母开头(、
、
),"< 3" 这类不是标签。
- const inner: string = trimmed.substring(1, close).trim();
- if (inner.length === 0) {
- return false;
- }
- const head: string = inner.charAt(0) === '/' ? inner.substring(1) : inner;
- const tagName: RegExp = new RegExp('^[a-zA-Z][a-zA-Z0-9]*');
- return tagName.test(head);
-}
-
-/**
- * 把一段内容包成可交给 RichText 渲染的最小 HTML 文档。
- *
- * 为什么用 RichText 而不是 Web:screensue 的内容是 agent 下发的第三方文本,
- * Web 组件默认带 JS 与网络能力(javaScriptAccess/fileAccess),等于让远端内容
- * 在客户端进程里执行脚本。RichText 只解析 HTML 子集、无脚本、无网络,
- * 对「给用户看一段内容」这个用途正好够用且更安全。
- *
- * RichText 用系统默认前景色,深色主题下会变成黑字看不见 —— 这里显式注入
- * 颜色/字号,让两种主题下都可读。
- */
-export function screensueHtml(content: string, dark: boolean): string {
- const fg: string = dark ? '#E8ECF4' : '#1B2430';
- const trimmed: string = content.trim();
- // 已是完整文档就不要重复包 (RichText 需要单一根节点才稳定)。
- const body: string = looksLikeHtml(trimmed) ? trimmed : escapeHtmlText(trimmed);
- return '
' + body + '
';
-}
-
-/** HTML 转义(纯文本走 RichText 时用,避免内容里的 < > 被当成标签)。 */
-export function escapeHtmlText(s: string): string {
- let out: string = '';
- for (let i = 0; i < s.length; i++) {
- const c: string = s.charAt(i);
- if (c === '&') {
- out += '&';
- } else if (c === '<') {
- out += '<';
- } else if (c === '>') {
- out += '>';
- } else if (c === '"') {
- out += '"';
- } else {
- out += c;
- }
- }
- return out;
-}
diff --git a/cmd/ohos/HomeAgent/entry/src/main/ets/common/BridgeRouter.ets b/cmd/ohos/HomeAgent/entry/src/main/ets/common/BridgeRouter.ets
index a6eabe1..1aa4d12 100644
--- a/cmd/ohos/HomeAgent/entry/src/main/ets/common/BridgeRouter.ets
+++ b/cmd/ohos/HomeAgent/entry/src/main/ets/common/BridgeRouter.ets
@@ -9,9 +9,8 @@ import {
capSpeakerUse,
capDeviceInfo,
capStatus,
- parseScreensue,
- ScreensuePayload,
} from './BridgeCaps';
+import { parseScreensue, ScreensuePayload } from './ScreensueHtml';
import { connStore } from './ConnStore';
import { common } from '@kit.AbilityKit';
diff --git a/cmd/ohos/HomeAgent/entry/src/main/ets/common/ScreensueHtml.ets b/cmd/ohos/HomeAgent/entry/src/main/ets/common/ScreensueHtml.ets
new file mode 100644
index 0000000..132f7ad
--- /dev/null
+++ b/cmd/ohos/HomeAgent/entry/src/main/ets/common/ScreensueHtml.ets
@@ -0,0 +1,182 @@
+/**
+ * screensue 载荷解析与 HTML 渲染(无 UI 依赖)。
+ *
+ * 从 BridgeCaps.ets 抽出:加进 HTML 检测/编码后那个文件超过 520 行,
+ * 超出工程「单文件 ≤400 行」的约定;而「screensue 内容怎么解析、怎么渲染」
+ * 与「设备能力怎么实现」本就是两件事。
+ */
+
+import { util } from '@kit.ArkTS';
+
+// ===== screensue 内容解析 =====
+// 服务端协议: screensue [秒] <内容>;0=常驻。
+
+export interface ScreensuePayload {
+ duration: number; // 秒;0 表示常驻直到用户关闭
+ content: string;
+}
+
+export function parseScreensue(rawArgs: string): ScreensuePayload {
+ const p: ScreensuePayload = { duration: 5, content: '' };
+ const leadingSpaces: RegExp = new RegExp('^\\s+');
+ const firstSpace: RegExp = new RegExp('\\s');
+ let rest: string = rawArgs.replace(leadingSpaces, '');
+ const splitAt: number = rest.search(firstSpace);
+ if (splitAt > 0) {
+ const first: string = rest.substring(0, splitAt);
+ const digits: RegExp = new RegExp('^\\d+$');
+ if (digits.test(first)) {
+ p.duration = Math.min(parseInt(first, 10), 86400);
+ rest = rest.substring(splitAt).replace(leadingSpaces, '');
+ }
+ }
+ p.content = rest;
+ return p;
+}
+
+/**
+ * 判断 agent 下发的 screensue 内容是不是 HTML。
+ *
+ * 服务端两侧协议都允许 HTML(localuse 的 local_screensue 在 Linux 用 browsh/w3m
+ * 渲染 HTML;remotedevice 的工具说明写的就是「显示内容/HTML」)。
+ *
+ * ★ 判据必须容忍前导杂质:实测 agent 常把整段文档连引号一起传进来
+ * (`'…'`),而"首个非空字符必须是 '<'"的旧判据直接判否、
+ * 退回纯文本渲染,用户看到的就是满屏标签源码(截图取证)。
+ *
+ * 所以这里扫到第一个「像标签开头」的 '<',不要求它在开头;但只有后面紧根
+ * 字母或 '/' 时才认,避免把 "a < b" 这类文本里的比较符当标签。
+ */
+export function looksLikeHtml(content: string): boolean {
+ return findHtmlStart(content) >= 0;
+}
+
+/** 找到第一个「像标签开头」的 '<';没有则 -1。 */
+function findHtmlStart(content: string): number {
+ for (let i = 0; i < content.length; i++) {
+ if (content.charAt(i) !== '<') {
+ continue;
+ }
+ const next: string = i + 1 < content.length ? content.charAt(i + 1) : '';
+ if (next === '/') {
+ const after: string = i + 2 < content.length ? content.charAt(i + 2) : '';
+ if (isAsciiLetter(after)) {
+ return i;
+ }
+ continue;
+ }
+ if (isAsciiLetter(next)) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+function isAsciiLetter(ch: string): boolean {
+ if (ch.length === 0) {
+ return false;
+ }
+ const c: number = ch.charCodeAt(0);
+ return (c >= 65 && c <= 90) || (c >= 97 && c <= 122);
+}
+
+/**
+ * 取出真正的 HTML 片段:剥掉 agent 误带的包裹引号,再从头截到第一个标签。
+ *
+ * 剥引号是必须的:不剥的话那个孤立的 `'` 会被 Web 当正文渲染出来
+ * (截图上第一行就是它),而且它还会把后续判据带偏。返回 '' 表示不是 HTML。
+ */
+export function screensueHtmlDocument(content: string): string {
+ let body: string = content.trim();
+ // 反复剥成对的包裹引号(agent 把整段 HTML 当命令参数传时的常见形态)。
+ while (body.length >= 2) {
+ const first: string = body.charAt(0);
+ const last: string = body.charAt(body.length - 1);
+ if ((first === '\'' && last === '\'') || (first === '"' && last === '"')) {
+ body = body.substring(1, body.length - 1).trim();
+ continue;
+ }
+ break;
+ }
+ const idx: number = findHtmlStart(body);
+ if (idx < 0) {
+ return '';
+ }
+ if (idx > 0) {
+ body = body.substring(idx);
+ }
+ return body;
+}
+
+/**
+ * 把 screensue 内容编成可直接交给 Web 组件 `loadData` 的 base64。
+ *
+ * 为什么必须上 Web(不再用 RichText):RichText 只认极小标签子集,
+ * 对 '
+ + fragment + '