Files
Artemis-Eapil/apps/showcase_ui/src/app/components/task-id-copy/task-id-copy.component.ts
T
chenmingxuan fac4ccb760 feat: prepare 2.2.0 with UI and execution fixes
Add dark theme, richer Markdown and readable plugin results.
Improve Android input evidence, response integrity and diagnostics.
Unify lifecycle, guidance receipts and evidence sources across clients.
Keep direct action dispatch without an added AI quota approval layer.
Align version metadata at 2.2.0 and bundle Helper 1.2.2.

Validation: 3063 backend tests before the version bump; 44 after.
Frontend: 317 tests and production build passed.
2026-10-26 00:44:13 +08:00

67 lines
2.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Clipboard } from '@angular/cdk/clipboard';
import { Component, DestroyRef, Input, inject, signal } from '@angular/core';
@Component({
selector: 'app-task-id-copy',
standalone: true,
template: `
<span class="copy-target" role="button" tabindex="0"
[attr.aria-label]="'复制任务 ID:' + taskId" title="点击复制任务 ID,也可选中文字复制"
(click)="copy($event)" (keydown.enter)="copy($event)" (keydown.space)="copy($event)">
<span class="value">ID: {{ taskId }}</span>
<span class="material-symbols-outlined" aria-hidden="true">content_copy</span>
</span>
<span class="feedback" role="status" aria-live="polite">{{ feedback() }}</span>
`,
styles: [`
:host { display: block; min-width: 0; font-size: 11px; color: inherit; user-select: text; }
.copy-target { display: inline-flex; align-items: center; gap: 5px; max-width: 100%;
padding: 3px 0; cursor: pointer; user-select: text; border-radius: 3px; }
.value { min-width: 0; overflow-wrap: anywhere; user-select: text; }
.material-symbols-outlined { flex-shrink: 0; font-size: 14px; user-select: none; }
.copy-target:hover { color: var(--app-primary-text, #2563eb); }
.copy-target:focus-visible { outline: 2px solid #60a5fa; outline-offset: 2px; }
.feedback { display: block; line-height: 1.6; user-select: text; }
.feedback:empty { display: none; }
`]
})
export class TaskIdCopyComponent {
@Input({ required: true }) taskId = '';
readonly feedback = signal('');
private readonly clipboard = inject(Clipboard);
private readonly destroyRef = inject(DestroyRef);
private timer: ReturnType<typeof setTimeout> | undefined;
constructor() {
this.destroyRef.onDestroy(() => clearTimeout(this.timer));
}
async copy(event: Event): Promise<void> {
event.stopPropagation();
// A drag selection must remain available for the browser's normal copy action.
const selection = window.getSelection();
if (event.type === 'click' && selection && !selection.isCollapsed
&& (event.currentTarget as HTMLElement).contains(selection.anchorNode)) return;
event.preventDefault();
let copied = false;
try {
if (navigator.clipboard?.writeText) {
await navigator.clipboard.writeText(this.taskId);
copied = true;
} else {
copied = this.clipboard.copy(this.taskId);
}
} catch {
try {
copied = this.clipboard.copy(this.taskId);
} catch {
// Keep manual selection available when the browser blocks both clipboard APIs.
}
}
if (this.destroyRef.destroyed) return;
this.feedback.set(copied ? '任务 ID 已复制' : '复制失败,请选中任务 ID 手动复制');
clearTimeout(this.timer);
this.timer = setTimeout(() => this.feedback.set(''), 3000);
}
}