@@ -33,8 +33,17 @@ export class PagesConnectComponent implements OnInit, OnDestroy {
3333 public isActive : boolean = true ;
3434 public isTimerStopped : boolean = false ;
3535 public showActionIcons : boolean = false ;
36+ public isTimerVisible : boolean = true ;
37+ public isTimerDragging : boolean = false ;
38+ public timerPosition : { x : number ; y : number } | null = null ;
3639 private readonly guiComponents : Set < string > = new Set ( [ 'lion' , 'tinker' , 'razor' , 'panda' ] ) ;
3740 private readonly terminalComponents : Set < string > = new Set ( [ 'koko' , 'chen' , 'magnus' , 'nec' ] ) ;
41+ private readonly timerPositionStorageKey = 'luna.connect.timer.position' ;
42+ private timerPointerId : number | null = null ;
43+ private timerDragStart : { x : number ; y : number } | null = null ;
44+ private timerDragOffset : { x : number ; y : number } | null = null ;
45+ private timerDragSize : { width : number ; height : number } | null = null ;
46+ private timerStartRect : { left : number ; top : number } | null = null ;
3847
3948 // 人脸在线相关
4049 private faceMonitorToken : string ;
@@ -85,6 +94,8 @@ export class PagesConnectComponent implements OnInit, OnDestroy {
8594 async ngOnInit ( ) {
8695 this . view = null ;
8796 this . isTimerStopped = false ;
97+ this . isTimerVisible = true ;
98+ this . loadTimerPosition ( ) ;
8899
89100 this . checkDirectMode ( ) ;
90101
@@ -507,6 +518,153 @@ export class PagesConnectComponent implements OnInit, OnDestroy {
507518 this . showActionIcons = event . clientY <= 65 ;
508519 }
509520
521+ public hideTimer ( ) : void {
522+ this . isTimerVisible = false ;
523+ }
524+
525+ public getTimerContainerStyle ( ) : Record < string , string > {
526+ if ( ! this . timerPosition ) {
527+ return { } ;
528+ }
529+ return {
530+ left : `${ this . timerPosition . x } px` ,
531+ top : `${ this . timerPosition . y } px` ,
532+ right : 'auto' ,
533+ bottom : 'auto' ,
534+ transform : 'none'
535+ } ;
536+ }
537+
538+ public onTimerPointerDown ( event : PointerEvent ) : void {
539+ if ( event . pointerType === 'mouse' && event . button !== 0 ) {
540+ return ;
541+ }
542+
543+ const timerContainer = event . currentTarget as HTMLElement | null ;
544+ if ( ! timerContainer ) {
545+ return ;
546+ }
547+
548+ const rect = timerContainer . getBoundingClientRect ( ) ;
549+
550+ this . timerPointerId = event . pointerId ;
551+ this . timerDragStart = { x : event . clientX , y : event . clientY } ;
552+ this . timerDragOffset = { x : event . clientX - rect . left , y : event . clientY - rect . top } ;
553+ this . timerDragSize = { width : rect . width , height : rect . height } ;
554+ this . timerStartRect = { left : rect . left , top : rect . top } ;
555+ this . isTimerDragging = false ;
556+
557+ timerContainer . setPointerCapture ( event . pointerId ) ;
558+ }
559+
560+ public onTimerPointerMove ( event : PointerEvent ) : void {
561+ if ( this . timerPointerId !== event . pointerId ) {
562+ return ;
563+ }
564+ if (
565+ ! this . timerDragStart ||
566+ ! this . timerDragOffset ||
567+ ! this . timerDragSize ||
568+ ! this . timerStartRect
569+ ) {
570+ return ;
571+ }
572+
573+ const dx = event . clientX - this . timerDragStart . x ;
574+ const dy = event . clientY - this . timerDragStart . y ;
575+ const dragThresholdPx = 3 ;
576+
577+ if ( ! this . isTimerDragging ) {
578+ if ( Math . abs ( dx ) < dragThresholdPx && Math . abs ( dy ) < dragThresholdPx ) {
579+ return ;
580+ }
581+ this . isTimerDragging = true ;
582+
583+ if ( ! this . timerPosition ) {
584+ this . timerPosition = { x : this . timerStartRect . left , y : this . timerStartRect . top } ;
585+ }
586+ }
587+
588+ event . preventDefault ( ) ;
589+
590+ const maxX = Math . max ( 0 , window . innerWidth - this . timerDragSize . width ) ;
591+ const maxY = Math . max ( 0 , window . innerHeight - this . timerDragSize . height ) ;
592+
593+ const unclampedX = event . clientX - this . timerDragOffset . x ;
594+ const unclampedY = event . clientY - this . timerDragOffset . y ;
595+
596+ this . timerPosition = {
597+ x : Math . min ( Math . max ( 0 , unclampedX ) , maxX ) ,
598+ y : Math . min ( Math . max ( 0 , unclampedY ) , maxY )
599+ } ;
600+ }
601+
602+ public onTimerPointerUp ( event : PointerEvent ) : void {
603+ if ( this . timerPointerId !== event . pointerId ) {
604+ return ;
605+ }
606+
607+ const timerContainer = event . currentTarget as HTMLElement | null ;
608+ if ( timerContainer ) {
609+ try {
610+ timerContainer . releasePointerCapture ( event . pointerId ) ;
611+ } catch { }
612+ }
613+
614+ if ( this . isTimerDragging ) {
615+ this . persistTimerPosition ( ) ;
616+ }
617+
618+ this . timerPointerId = null ;
619+ this . timerDragStart = null ;
620+ this . timerDragOffset = null ;
621+ this . timerDragSize = null ;
622+ this . timerStartRect = null ;
623+ this . isTimerDragging = false ;
624+ }
625+
626+ private loadTimerPosition ( ) : void {
627+ try {
628+ const raw = localStorage . getItem ( this . timerPositionStorageKey ) ;
629+ if ( ! raw ) {
630+ return ;
631+ }
632+ const parsed = JSON . parse ( raw ) as { x ?: unknown ; y ?: unknown ; w ?: unknown ; h ?: unknown } ;
633+ if ( typeof parsed . x !== 'number' || typeof parsed . y !== 'number' ) {
634+ return ;
635+ }
636+
637+ const width = typeof parsed . w === 'number' && parsed . w > 0 ? parsed . w : 0 ;
638+ const height = typeof parsed . h === 'number' && parsed . h > 0 ? parsed . h : 0 ;
639+
640+ const maxX = width > 0 ? Math . max ( 0 , window . innerWidth - width ) : window . innerWidth ;
641+ const maxY = height > 0 ? Math . max ( 0 , window . innerHeight - height ) : window . innerHeight ;
642+
643+ this . timerPosition = {
644+ x : Math . min ( Math . max ( 0 , parsed . x ) , maxX ) ,
645+ y : Math . min ( Math . max ( 0 , parsed . y ) , maxY )
646+ } ;
647+ } catch { }
648+ }
649+
650+ private persistTimerPosition ( ) : void {
651+ if ( ! this . timerPosition ) {
652+ return ;
653+ }
654+
655+ try {
656+ localStorage . setItem (
657+ this . timerPositionStorageKey ,
658+ JSON . stringify ( {
659+ x : this . timerPosition . x ,
660+ y : this . timerPosition . y ,
661+ w : this . timerDragSize ?. width ?? 0 ,
662+ h : this . timerDragSize ?. height ?? 0
663+ } )
664+ ) ;
665+ } catch { }
666+ }
667+
510668 /**
511669 * 开始计时器
512670 */
0 commit comments