Skip to content

Commit f6c51a3

Browse files
committed
Restrict UI text selection to app data
1 parent 43be610 commit f6c51a3

6 files changed

Lines changed: 98 additions & 35 deletions

File tree

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@
9898
- WebUI CSS gzip budget is intentionally tight. Before raising the budget, prefer lazy CSS for non-first-screen UI (`runtime-log.css`, `lcapps-bubble.css`), removing duplicated control rules, or consolidating existing tokens. A modest budget raise is acceptable only when the transferred gzip size and first-screen impact are understood.
9999
- Runtime log UI is page-session frontend diagnostics only: live while the page is open, capped, not Cloudflare logs, and not persisted across refresh. Keep runtime log event names/fields in English; localize only visible labels.
100100
- For slow Web UI link analysis, inspect runtime log split timings (`client_duration_ms`, `server_duration_ms`, `fetch_headers_ms`, `response_text_ms`, `json_parse_ms`, `render_ms`) before blaming rendering, history writes, or remote parsing. Large reports can justify moving noncritical work off the visible-result path, but they are not evidence of multi-second stalls by themselves.
101+
- Static Web UI text should stay non-selectable for drag/long-press copy. Only application/report data values should opt into selection, using the existing `app-data-text` whitelist class or an equivalent narrowly scoped report-data selector.
101102

102103
## Worker Notes
103104

packages/apk-webui/src/app.css

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,32 @@ body {
188188
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", "PingFang SC", "Segoe UI", sans-serif;
189189
-webkit-font-smoothing: antialiased;
190190
text-rendering: optimizeLegibility;
191+
-webkit-touch-callout: none;
192+
-webkit-user-select: none;
193+
user-select: none;
194+
}
195+
196+
input,
197+
textarea,
198+
[contenteditable="true"] {
199+
-webkit-touch-callout: default;
200+
-webkit-user-select: text;
201+
user-select: text;
202+
}
203+
204+
select {
205+
-webkit-user-select: auto;
206+
user-select: auto;
207+
}
208+
209+
.app-data-text,
210+
.app-data-text *,
211+
.history-list :is(.history-title, .history-package, .history-badge-value, .history-stat-value),
212+
.history-file-line span:not([aria-hidden="true"]),
213+
.lcapps-picker__grid :is(.lcapps-app-title, .lcapps-app-package, .lcapps-app-meta) {
214+
-webkit-touch-callout: default;
215+
-webkit-user-select: text;
216+
user-select: text;
191217
}
192218

193219
.color-orb-background {

packages/apk-webui/src/app/compare-controller.js

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -542,12 +542,12 @@ export class CompareController {
542542
`<div class="compare-app-card">`,
543543
this.renderAppIcon(info),
544544
`<div class="compare-app-copy">`,
545-
`<h3>${escapeHtml(info.appName || this.t("unknown"))}</h3>`,
546-
`<code>${escapeHtml(info.packageName || this.t("unknown"))}</code>`,
545+
`<h3 class="app-data-text">${escapeHtml(info.appName || this.t("unknown"))}</h3>`,
546+
`<code class="app-data-text">${escapeHtml(info.packageName || this.t("unknown"))}</code>`,
547547
`<div class="hero-meta">`,
548548
chip(`${this.t("versionName")}: ${info.versionName || this.t("unknown")}`, this.t("unknown")),
549549
chip(`${this.t("versionCode")}: ${info.versionCode || this.t("unknown")}`, this.t("unknown")),
550-
chip(sourceLabel, this.t("unknown")),
550+
chip(sourceLabel, this.t("unknown"), { appData: false }),
551551
`</div>`,
552552
`<div class="compare-stat-strip">`,
553553
compareMiniStat(this.t("permissions"), stats.permissions),
@@ -653,8 +653,8 @@ export class CompareController {
653653
return [
654654
`<div class="compare-table-row${changed}">`,
655655
`<div>${escapeHtml(label)}</div>`,
656-
`<div>${escapeHtml(leftValue)}</div>`,
657-
`<div>${escapeHtml(rightValue)}</div>`,
656+
`<div class="app-data-text">${escapeHtml(leftValue)}</div>`,
657+
`<div class="app-data-text">${escapeHtml(rightValue)}</div>`,
658658
`</div>`,
659659
].join("");
660660
}).join(""),
@@ -755,10 +755,10 @@ export class CompareController {
755755
return [
756756
`<article class="compare-diff-item${status ? ` is-${escapeAttr(status)}` : ""}">`,
757757
`<div class="compare-diff-item-header">`,
758-
`<strong>${item.labelHtml || escapeHtml(item.label || item.key || this.t("unknown"))}</strong>`,
758+
`<strong class="app-data-text">${item.labelHtml || escapeHtml(item.label || item.key || this.t("unknown"))}</strong>`,
759759
statusLabel ? `<span class="compare-diff-status">${escapeHtml(statusLabel)}</span>` : "",
760760
`</div>`,
761-
item.metaHtml || item.meta ? `<span>${item.metaHtml || escapeHtml(item.meta)}</span>` : "",
761+
item.metaHtml || item.meta ? `<span class="app-data-text">${item.metaHtml || escapeHtml(item.meta)}</span>` : "",
762762
`</article>`,
763763
].join("");
764764
}
@@ -942,9 +942,9 @@ export class CompareController {
942942

943943
renderPairMeta(left, right) {
944944
return [
945-
`<span class="compare-diff-pair-value">${left.pairValueHtml || escapeHtml(left.pairValue || left.meta || left.label || this.t("unknown"))}</span>`,
945+
`<span class="compare-diff-pair-value app-data-text">${left.pairValueHtml || escapeHtml(left.pairValue || left.meta || left.label || this.t("unknown"))}</span>`,
946946
`<span class="compare-diff-arrow" aria-hidden="true">→</span>`,
947-
`<span class="compare-diff-pair-value">${right.pairValueHtml || escapeHtml(right.pairValue || right.meta || right.label || this.t("unknown"))}</span>`,
947+
`<span class="compare-diff-pair-value app-data-text">${right.pairValueHtml || escapeHtml(right.pairValue || right.meta || right.label || this.t("unknown"))}</span>`,
948948
].join("");
949949
}
950950

@@ -1207,7 +1207,7 @@ function compareMiniStat(label, value) {
12071207
return [
12081208
`<span class="compare-mini-stat">`,
12091209
`<span>${escapeHtml(label)}</span>`,
1210-
`<strong>${escapeHtml(String(value || 0))}</strong>`,
1210+
`<strong class="app-data-text">${escapeHtml(String(value || 0))}</strong>`,
12111211
`</span>`,
12121212
].join("");
12131213
}
@@ -1216,7 +1216,7 @@ function metric(label, value) {
12161216
return [
12171217
`<article class="metric-card">`,
12181218
`<div class="metric-label">${escapeHtml(label)}</div>`,
1219-
`<div class="metric-value">${escapeHtml(String(value))}</div>`,
1219+
`<div class="metric-value app-data-text">${escapeHtml(String(value))}</div>`,
12201220
`</article>`,
12211221
].join("");
12221222
}
@@ -1229,8 +1229,9 @@ function sectionStack(items) {
12291229
return `<div class="section-stack">${items.join("")}</div>`;
12301230
}
12311231

1232-
function chip(value, unknownLabel) {
1233-
return `<span class="chip">${escapeHtml(value || unknownLabel)}</span>`;
1232+
function chip(value, unknownLabel, { appData = true } = {}) {
1233+
const className = appData ? "chip app-data-text" : "chip";
1234+
return `<span class="${className}">${escapeHtml(value || unknownLabel)}</span>`;
12341235
}
12351236

12361237
function emptyList(message) {

packages/apk-webui/src/app/report-renderer.js

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ function renderHero(report) {
9999
chip(`${t("heroVersionName")}: ${info.versionName || t("unknown")}`),
100100
chip(`${t("heroVersionCode")}: ${info.versionCode || t("unknown")}`),
101101
chip(`${t("targetSdk")}: ${info.targetSdk || t("unknown")}`),
102-
chip(t("localFile")),
102+
chip(t("localFile"), { appData: false }),
103103
`</div>`,
104104
`</div>`,
105105
].join("");
@@ -359,9 +359,9 @@ function renderNativeTab(report) {
359359
const sdk = library.sdk ? renderSdkChip(library.sdk) : "";
360360
return [
361361
`<article class="list-row native-library-row">`,
362-
`<div class="row-title"><span>${escapeHtml(library.name || t("unknown"))}</span></div>`,
363-
`<div class="row-meta native-library-meta"><span>${escapeHtml(t("size"))}: ${escapeHtml(formatBytes(library.size || 0))}</span>${renderNativeLibraryLabels(library)}</div>`,
364-
sdk ? `<div class="row-meta native-library-sdk">${sdk}</div>` : "",
362+
`<div class="row-title"><span class="app-data-text">${escapeHtml(library.name || t("unknown"))}</span></div>`,
363+
`<div class="row-meta native-library-meta app-data-text"><span>${escapeHtml(t("size"))}: ${escapeHtml(formatBytes(library.size || 0))}</span>${renderNativeLibraryLabels(library)}</div>`,
364+
sdk ? `<div class="row-meta native-library-sdk app-data-text">${sdk}</div>` : "",
365365
`</article>`,
366366
].join("");
367367
}).join("");
@@ -422,7 +422,7 @@ function renderAppTitle(title) {
422422
const value = title || t("unknown");
423423
return [
424424
`<h2 class="app-title-mask" data-app-title-mask>`,
425-
`<span class="app-title-mask__base">${escapeHtml(value)}</span>`,
425+
`<span class="app-title-mask__base app-data-text">${escapeHtml(value)}</span>`,
426426
`<span class="app-title-mask__color" aria-hidden="true">${escapeHtml(value)}</span>`,
427427
`</h2>`,
428428
].join("");
@@ -457,7 +457,7 @@ function renderComponentRow(component) {
457457
`<article class="list-row component-row">`,
458458
`<div class="component-row-header">`,
459459
`<div class="component-row-main">`,
460-
`<div class="row-title component-row-title"><span>${escapeHtml(component.name || t("unknown"))}</span></div>`,
460+
`<div class="row-title component-row-title"><span class="app-data-text">${escapeHtml(component.name || t("unknown"))}</span></div>`,
461461
`</div>`,
462462
`</div>`,
463463
renderComponentDetails(component),
@@ -644,9 +644,9 @@ function renderMetaDataTab(report) {
644644

645645
const rows = metaData.map((item) => [
646646
`<article class="list-row">`,
647-
`<div class="row-title"><span>${escapeHtml(item.name || t("unknown"))}</span></div>`,
648-
`<div class="row-meta">${renderMetaDataValue(item)}</div>`,
649-
item.resourceId != null ? `<div class="row-meta">${escapeHtml(t("resource"))}: ${codeChip(formatResourceId(item.resourceId))}</div>` : "",
647+
`<div class="row-title"><span class="app-data-text">${escapeHtml(item.name || t("unknown"))}</span></div>`,
648+
`<div class="row-meta app-data-text">${renderMetaDataValue(item)}</div>`,
649+
item.resourceId != null ? `<div class="row-meta app-data-text">${escapeHtml(t("resource"))}: ${codeChip(formatResourceId(item.resourceId))}</div>` : "",
650650
`</article>`,
651651
].join("")).join("");
652652

@@ -673,7 +673,7 @@ function renderRawTab(report) {
673673
`<span>${escapeHtml(t("exportJson"))}</span>`,
674674
`</button>`,
675675
`</div>`,
676-
`<pre class="json-block">${escapeHtml(formatExportJson(report))}</pre>`,
676+
`<pre class="json-block app-data-text">${escapeHtml(formatExportJson(report))}</pre>`,
677677
].join("");
678678
}
679679

@@ -720,12 +720,12 @@ function renderSdkRows(entries) {
720720
return [
721721
`<article class="sdk-row">`,
722722
`<div class="sdk-row-header">`,
723-
`<div class="sdk-title">${renderSdkIcon(entry.iconUrl, entry.label, entry.singleColorIcon)}${renderSdkRuleLabel(entry, t("unknown"))}</div>`,
724-
`<span class="sdk-count">${escapeHtml(String(entry.count || 0))}</span>`,
723+
`<div class="sdk-title app-data-text">${renderSdkIcon(entry.iconUrl, entry.label, entry.singleColorIcon)}${renderSdkRuleLabel(entry, t("unknown"))}</div>`,
724+
`<span class="sdk-count app-data-text">${escapeHtml(String(entry.count || 0))}</span>`,
725725
`</div>`,
726726
`<div class="bar-track"><div class="bar" style="width: ${width}%"></div></div>`,
727-
detail ? `<div class="sdk-meta">${escapeHtml(detail)}</div>` : "",
728-
preview ? `<div class="sdk-preview">${preview}</div>` : "",
727+
detail ? `<div class="sdk-meta app-data-text">${escapeHtml(detail)}</div>` : "",
728+
preview ? `<div class="sdk-preview app-data-text">${preview}</div>` : "",
729729
`</article>`,
730730
].join("");
731731
}).join("");
@@ -773,7 +773,7 @@ function renderFeaturePills(buildFeatures = {}) {
773773
return "";
774774
}
775775

776-
return `<div class="feature-grid">${features.map((item) => `<span class="feature-pill">${escapeHtml(item)}</span>`).join("")}</div>`;
776+
return `<div class="feature-grid">${features.map((item) => `<span class="feature-pill app-data-text">${escapeHtml(item)}</span>`).join("")}</div>`;
777777
}
778778

779779
function renderSdkChip(sdk) {
@@ -808,7 +808,7 @@ function renderKeyValueTable(rows) {
808808
rows.map(([label, value]) => [
809809
`<div class="kv-row">`,
810810
`<div class="kv-label">${escapeHtml(label)}</div>`,
811-
`<div class="kv-value">${escapeHtml(value || t("unknown"))}</div>`,
811+
`<div class="kv-value app-data-text">${escapeHtml(value || t("unknown"))}</div>`,
812812
`</div>`,
813813
].join("")).join(""),
814814
`</div>`,
@@ -822,7 +822,7 @@ function renderHtmlKeyValueTable(rows, className = "") {
822822
rows.map(([label, value]) => [
823823
`<div class="kv-row">`,
824824
`<div class="kv-label">${escapeHtml(label)}</div>`,
825-
`<div class="kv-value">${value || escapeHtml(t("unknown"))}</div>`,
825+
`<div class="kv-value app-data-text">${value || escapeHtml(t("unknown"))}</div>`,
826826
`</div>`,
827827
].join("")).join(""),
828828
`</div>`,
@@ -833,7 +833,7 @@ function metric(label, value) {
833833
return [
834834
`<article class="metric-card">`,
835835
`<div class="metric-label">${escapeHtml(label)}</div>`,
836-
`<div class="metric-value">${escapeHtml(String(value))}</div>`,
836+
`<div class="metric-value app-data-text">${escapeHtml(String(value))}</div>`,
837837
`</article>`,
838838
].join("");
839839
}
@@ -846,16 +846,17 @@ function sectionStack(items) {
846846
return `<div class="section-stack">${items.join("")}</div>`;
847847
}
848848

849-
function chip(value) {
850-
return `<span class="chip">${escapeHtml(value || t("unknown"))}</span>`;
849+
function chip(value, { appData = true } = {}) {
850+
const className = appData ? "chip app-data-text" : "chip";
851+
return `<span class="${className}">${escapeHtml(value || t("unknown"))}</span>`;
851852
}
852853

853854
function codeChip(value) {
854-
return `<span class="code-chip">${escapeHtml(value || t("unknown"))}</span>`;
855+
return `<span class="code-chip app-data-text">${escapeHtml(value || t("unknown"))}</span>`;
855856
}
856857

857858
function inlineCodeValue(value) {
858-
return `<code class="inline-code-value">${escapeHtml(value || t("unknown"))}</code>`;
859+
return `<code class="inline-code-value app-data-text">${escapeHtml(value || t("unknown"))}</code>`;
859860
}
860861

861862
function buildFeatureLabel(name, version) {

packages/bot-worker/src/report-viewer.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,26 @@ function renderReportPage(page, locale) {
7777
radial-gradient(circle at top left, rgba(236, 72, 153, 0.08), transparent 24rem),
7878
var(--bg);
7979
color: var(--text);
80+
-webkit-touch-callout: none;
81+
-webkit-user-select: none;
82+
user-select: none;
83+
}
84+
85+
.report-hero h1,
86+
.report-hero .meta,
87+
pre,
88+
code,
89+
.kv-value,
90+
.feature-pill,
91+
.item-line,
92+
.chip-cloud,
93+
.sdk-chart-label,
94+
.sdk-chart-count,
95+
.sdk-chart-meta,
96+
.sdk-chart-preview {
97+
-webkit-touch-callout: default;
98+
-webkit-user-select: text;
99+
user-select: text;
80100
}
81101
82102
.page {

packages/bot-worker/src/upload-view.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,20 @@ export function renderUploadPage({
6767
var(--bg);
6868
color: var(--text);
6969
font-family: "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
70+
-webkit-touch-callout: none;
71+
-webkit-user-select: none;
72+
user-select: none;
73+
}
74+
75+
input {
76+
-webkit-touch-callout: default;
77+
-webkit-user-select: text;
78+
user-select: text;
79+
}
80+
81+
select {
82+
-webkit-user-select: auto;
83+
user-select: auto;
7084
}
7185
7286
main {

0 commit comments

Comments
 (0)