@@ -6920,6 +6920,7 @@ describe("XtermHost", () => {
69206920 } ) ;
69216921
69226922 store . set ( localeAtom , "en" ) ;
6923+ store . set ( terminalPreferencesAtom , { copyOnSelect : true } ) ;
69236924 store . set ( wsClientAtom , {
69246925 sendCommand : vi . fn ( ) . mockResolvedValue ( { ok : true , data : { status : "ok" } } ) ,
69256926 subscribe : vi . fn ( ( ) => ( ) => { } ) ,
@@ -7014,6 +7015,109 @@ describe("XtermHost", () => {
70147015 vi . useRealTimers ( ) ;
70157016 } ) ;
70167017
7018+ it ( "does not enter mobile copy mode when copy-on-select is disabled" , async ( ) => {
7019+ vi . useFakeTimers ( ) ;
7020+ viewportMocks . viewport = "mobile" ;
7021+
7022+ const originalMatchMedia = window . matchMedia ;
7023+ const vibrate = vi . fn ( ) ;
7024+ const store = createStore ( ) ;
7025+
7026+ mockTerminal . cols = 4 ;
7027+ mockTerminal . rows = 2 ;
7028+
7029+ window . matchMedia = vi . fn ( ) . mockImplementation ( ( query : string ) => ( {
7030+ matches : query === "(pointer: coarse)" ,
7031+ media : query ,
7032+ onchange : null ,
7033+ addListener : vi . fn ( ) ,
7034+ removeListener : vi . fn ( ) ,
7035+ addEventListener : vi . fn ( ) ,
7036+ removeEventListener : vi . fn ( ) ,
7037+ dispatchEvent : vi . fn ( ) ,
7038+ } ) ) as typeof window . matchMedia ;
7039+
7040+ Object . defineProperty ( navigator , "vibrate" , {
7041+ configurable : true ,
7042+ value : vibrate ,
7043+ } ) ;
7044+
7045+ store . set ( localeAtom , "en" ) ;
7046+ store . set ( terminalPreferencesAtom , { copyOnSelect : false } ) ;
7047+ store . set ( wsClientAtom , {
7048+ sendCommand : vi . fn ( ) . mockResolvedValue ( { ok : true , data : { status : "ok" } } ) ,
7049+ subscribe : vi . fn ( ( ) => ( ) => { } ) ,
7050+ getStatus : vi . fn ( ( ) => "connected" ) ,
7051+ onStatus : vi . fn ( ( ) => ( ) => { } ) ,
7052+ sendTerminalInput : vi . fn ( ) . mockResolvedValue ( undefined ) ,
7053+ } as never ) ;
7054+
7055+ const { container } = render (
7056+ < Provider store = { store } >
7057+ < XtermHost terminalId = "mobile-copy-mode-disabled-terminal" workspaceId = "test-workspace" />
7058+ </ Provider >
7059+ ) ;
7060+
7061+ const host = container . querySelector ( ".xterm-host" ) as HTMLDivElement | null ;
7062+ expect ( host ) . toBeTruthy ( ) ;
7063+
7064+ vi . spyOn ( host ! , "getBoundingClientRect" ) . mockReturnValue ( {
7065+ width : 320 ,
7066+ height : 160 ,
7067+ top : 0 ,
7068+ right : 320 ,
7069+ bottom : 160 ,
7070+ left : 0 ,
7071+ x : 0 ,
7072+ y : 0 ,
7073+ toJSON : ( ) => ( { } ) ,
7074+ } as DOMRect ) ;
7075+
7076+ const rowsElement = document . createElement ( "div" ) ;
7077+ rowsElement . className = "xterm-rows" ;
7078+ vi . spyOn ( rowsElement , "getBoundingClientRect" ) . mockReturnValue ( {
7079+ width : 200 ,
7080+ height : 40 ,
7081+ top : 12 ,
7082+ right : 212 ,
7083+ bottom : 52 ,
7084+ left : 8 ,
7085+ x : 8 ,
7086+ y : 12 ,
7087+ toJSON : ( ) => ( { } ) ,
7088+ } as DOMRect ) ;
7089+
7090+ const row = document . createElement ( "div" ) ;
7091+ const span = document . createElement ( "span" ) ;
7092+ span . textContent = "ab" ;
7093+ row . appendChild ( span ) ;
7094+ rowsElement . appendChild ( row ) ;
7095+ host ! . appendChild ( rowsElement ) ;
7096+
7097+ const dispatchTouchEvent = (
7098+ type : string ,
7099+ touches : Array < { identifier : number ; clientY : number } > ,
7100+ changedTouches : Array < { identifier : number ; clientY : number } > = touches
7101+ ) => {
7102+ const event = new Event ( type , { bubbles : true , cancelable : true } ) ;
7103+ Object . defineProperty ( event , "touches" , { value : touches } ) ;
7104+ Object . defineProperty ( event , "changedTouches" , { value : changedTouches } ) ;
7105+ host ?. dispatchEvent ( event ) ;
7106+ } ;
7107+
7108+ dispatchTouchEvent ( "touchstart" , [ { identifier : 1 , clientY : 120 } ] ) ;
7109+
7110+ await act ( async ( ) => {
7111+ vi . advanceTimersByTime ( 500 ) ;
7112+ } ) ;
7113+
7114+ expect ( container . querySelector ( ".mobile-terminal-copy-mode" ) ) . toBeNull ( ) ;
7115+ expect ( vibrate ) . not . toHaveBeenCalled ( ) ;
7116+
7117+ window . matchMedia = originalMatchMedia ;
7118+ vi . useRealTimers ( ) ;
7119+ } ) ;
7120+
70177121 it ( "does not enter mobile copy mode when the gesture becomes a scroll" , async ( ) => {
70187122 vi . useFakeTimers ( ) ;
70197123 viewportMocks . viewport = "mobile" ;
@@ -7034,10 +7138,14 @@ describe("XtermHost", () => {
70347138 dispatchEvent : vi . fn ( ) ,
70357139 } ) ) as typeof window . matchMedia ;
70367140
7141+ const store = createStore ( ) ;
7142+ store . set ( localeAtom , "en" ) ;
7143+ store . set ( terminalPreferencesAtom , { copyOnSelect : true } ) ;
7144+
70377145 const { container } = render (
7038- < JotaiProvider >
7146+ < Provider store = { store } >
70397147 < XtermHost terminalId = "mobile-copy-mode-scroll-terminal" workspaceId = "test-workspace" />
7040- </ JotaiProvider >
7148+ </ Provider >
70417149 ) ;
70427150
70437151 const host = container . querySelector ( ".xterm-host" ) ;
@@ -7095,13 +7203,17 @@ describe("XtermHost", () => {
70957203 value : vibrate ,
70967204 } ) ;
70977205
7206+ const store = createStore ( ) ;
7207+ store . set ( localeAtom , "en" ) ;
7208+ store . set ( terminalPreferencesAtom , { copyOnSelect : true } ) ;
7209+
70987210 const { container } = render (
7099- < JotaiProvider >
7211+ < Provider store = { store } >
71007212 < XtermHost
71017213 terminalId = "mobile-copy-mode-horizontal-drift-terminal"
71027214 workspaceId = "test-workspace"
71037215 />
7104- </ JotaiProvider >
7216+ </ Provider >
71057217 ) ;
71067218
71077219 const host = container . querySelector ( ".xterm-host" ) ;
@@ -7190,6 +7302,7 @@ describe("XtermHost", () => {
71907302 } ) ) as typeof window . matchMedia ;
71917303
71927304 store . set ( localeAtom , "en" ) ;
7305+ store . set ( terminalPreferencesAtom , { copyOnSelect : true } ) ;
71937306 store . set ( wsClientAtom , {
71947307 sendCommand : vi . fn ( ) . mockResolvedValue ( { ok : true , data : { status : "ok" } } ) ,
71957308 subscribe : vi . fn ( ( ) => ( ) => { } ) ,
@@ -7306,13 +7419,17 @@ describe("XtermHost", () => {
73067419 } ) as typeof requestAnimationFrame ;
73077420 global . cancelAnimationFrame = vi . fn ( ) as typeof cancelAnimationFrame ;
73087421
7422+ const store = createStore ( ) ;
7423+ store . set ( localeAtom , "en" ) ;
7424+ store . set ( terminalPreferencesAtom , { copyOnSelect : true } ) ;
7425+
73097426 const { container } = render (
7310- < JotaiProvider >
7427+ < Provider store = { store } >
73117428 < XtermHost
73127429 terminalId = "mobile-copy-mode-below-tolerance-momentum-terminal"
73137430 workspaceId = "test-workspace"
73147431 />
7315- </ JotaiProvider >
7432+ </ Provider >
73167433 ) ;
73177434
73187435 const host = container . querySelector ( ".xterm-host" ) ;
@@ -7370,6 +7487,7 @@ describe("XtermHost", () => {
73707487 } ) ) as typeof window . matchMedia ;
73717488
73727489 store . set ( localeAtom , "en" ) ;
7490+ store . set ( terminalPreferencesAtom , { copyOnSelect : true } ) ;
73737491 store . set ( wsClientAtom , {
73747492 sendCommand : vi . fn ( ) . mockResolvedValue ( { ok : true , data : { status : "ok" } } ) ,
73757493 subscribe : vi . fn ( ( ) => ( ) => { } ) ,
@@ -7472,6 +7590,7 @@ describe("XtermHost", () => {
74727590 } ) ) as typeof window . matchMedia ;
74737591
74747592 store . set ( localeAtom , "en" ) ;
7593+ store . set ( terminalPreferencesAtom , { copyOnSelect : true } ) ;
74757594 store . set ( wsClientAtom , {
74767595 sendCommand : vi . fn ( ) . mockResolvedValue ( { ok : true , data : { status : "ok" } } ) ,
74777596 subscribe : vi . fn ( ( ) => ( ) => { } ) ,
@@ -7568,6 +7687,7 @@ describe("XtermHost", () => {
75687687 } ) ) as typeof window . matchMedia ;
75697688
75707689 store . set ( localeAtom , "en" ) ;
7690+ store . set ( terminalPreferencesAtom , { copyOnSelect : true } ) ;
75717691 store . set ( wsClientAtom , {
75727692 sendCommand : vi . fn ( ) . mockResolvedValue ( { ok : true , data : { status : "ok" } } ) ,
75737693 subscribe : vi . fn ( ( ) => ( ) => { } ) ,
@@ -7674,6 +7794,7 @@ describe("XtermHost", () => {
76747794 } ) ) as typeof window . matchMedia ;
76757795
76767796 store . set ( localeAtom , "en" ) ;
7797+ store . set ( terminalPreferencesAtom , { copyOnSelect : true } ) ;
76777798 store . set ( wsClientAtom , {
76787799 sendCommand : vi . fn ( ) . mockResolvedValue ( { ok : true , data : { status : "ok" } } ) ,
76797800 subscribe : vi . fn ( ( ) => ( ) => { } ) ,
@@ -7769,6 +7890,7 @@ describe("XtermHost", () => {
77697890 } ) ) as typeof window . matchMedia ;
77707891
77717892 store . set ( localeAtom , "zh" ) ;
7893+ store . set ( terminalPreferencesAtom , { copyOnSelect : true } ) ;
77727894 store . set ( wsClientAtom , {
77737895 sendCommand : vi . fn ( ) . mockResolvedValue ( { ok : true , data : { status : "ok" } } ) ,
77747896 subscribe : vi . fn ( ( ) => ( ) => { } ) ,
@@ -7903,6 +8025,7 @@ describe("XtermHost", () => {
79038025 } ) ) as typeof window . matchMedia ;
79048026
79058027 store . set ( localeAtom , "en" ) ;
8028+ store . set ( terminalPreferencesAtom , { copyOnSelect : true } ) ;
79068029 store . set ( wsClientAtom , {
79078030 sendCommand : vi . fn ( ) . mockResolvedValue ( { ok : true , data : { status : "ok" } } ) ,
79088031 subscribe : vi . fn ( ( ) => ( ) => { } ) ,
0 commit comments