11import * as constants from './constants.js' ;
22import * as applier from './blocklyApplier.js' ;
33import * as helper from './helper.js' ;
4+ import * as recorder from './recorder.js' ;
45
56const FLUSH_DELAY_MS = 80 ;
67const MAX_WAIT_MS = 600 ;
78const DRAG_RETRY_MS = 200 ;
89
910const dirtyBlocksByTarget = new Map ( ) ;
11+ const dirtyCommentsByTarget = new Map ( ) ;
1012const repairPending = new Set ( ) ;
1113let fullRefreshPending = false ;
1214let toolboxRefreshPending = false ;
@@ -17,13 +19,55 @@ function getWorkspace() {
1719 return constants . editorWorkspace ( ) ;
1820}
1921
22+ function holdInHand ( Blockly , workspace ) {
23+ const held = { selectedId : Blockly . selected ? Blockly . selected . id : null , field : null } ;
24+ const owner = Blockly . WidgetDiv . owner_ ;
25+ const input = Blockly . FieldTextInput ? Blockly . FieldTextInput . htmlInput_ : null ;
26+ if ( Blockly . WidgetDiv . isVisible ( ) && input && owner && owner . sourceBlock_ && owner . name &&
27+ owner . sourceBlock_ . workspace === workspace ) {
28+ held . field = {
29+ blockId : owner . sourceBlock_ . id ,
30+ name : owner . name ,
31+ value : input . value ,
32+ start : input . selectionStart ,
33+ end : input . selectionEnd
34+ } ;
35+ }
36+ return held ;
37+ }
38+
39+ function giveBack ( Blockly , workspace , held ) {
40+ if ( ! workspace || ! held ) return ;
41+ if ( held . selectedId && ( ! Blockly . selected || Blockly . selected . id !== held . selectedId ) ) {
42+ const block = workspace . getBlockById ( held . selectedId ) ;
43+ if ( block && typeof block . select === 'function' ) block . select ( ) ;
44+ }
45+ if ( ! held . field ) return ;
46+ const block = workspace . getBlockById ( held . field . blockId ) ;
47+ const field = block ? block . getField ( held . field . name ) : null ;
48+ if ( ! field || typeof field . showEditor_ !== 'function' || Blockly . WidgetDiv . isVisible ( ) ) return ;
49+ try {
50+ field . showEditor_ ( ) ;
51+ const input = Blockly . FieldTextInput . htmlInput_ ;
52+ if ( ! input ) return ;
53+ if ( input . value !== held . field . value ) input . value = held . field . value ;
54+ const length = input . value . length ;
55+ const start = Math . min ( held . field . start == null ? length : held . field . start , length ) ;
56+ const end = Math . min ( held . field . end == null ? length : held . field . end , length ) ;
57+ input . setSelectionRange ( start , end ) ;
58+ } catch ( e ) {
59+ }
60+ }
61+
2062export function rebuildWorkspace ( ) {
2163 const vm = constants . mutableRefs . vm ;
2264 if ( ! vm ) return ;
65+ const Blockly = constants . mutableRefs . BlocklyInstance ;
2366 const workspace = getWorkspace ( ) ;
2467 const targetBefore = vm . editingTarget ? vm . editingTarget . id : null ;
2568 const undoStack = workspace ? workspace . undoStack_ . slice ( ) : null ;
2669 const redoStack = workspace ? workspace . redoStack_ . slice ( ) : null ;
70+ const held = ( workspace && Blockly ) ? holdInHand ( Blockly , workspace ) : null ;
2771
2872 vm . emitWorkspaceUpdate ( ) ;
2973
@@ -32,6 +76,7 @@ export function rebuildWorkspace() {
3276 workspace . undoStack_ = undoStack ;
3377 workspace . redoStack_ = redoStack ;
3478 }
79+ if ( held && targetAfter === targetBefore ) giveBack ( Blockly , getWorkspace ( ) , held ) ;
3580}
3681
3782function claimsChild ( parent , childId ) {
@@ -82,7 +127,25 @@ export function queueBlockSync(targetId, blockIds) {
82127 arm ( ) ;
83128}
84129
130+ export function queueCommentSync ( targetId , commentIds ) {
131+ if ( ! targetId || ! commentIds || commentIds . length === 0 ) return ;
132+ const editingTarget = constants . mutableRefs . vm ?. editingTarget ;
133+ if ( ! editingTarget || editingTarget . id !== targetId ) return ;
134+ let set = dirtyCommentsByTarget . get ( targetId ) ;
135+ if ( ! set ) {
136+ set = new Set ( ) ;
137+ dirtyCommentsByTarget . set ( targetId , set ) ;
138+ }
139+ commentIds . forEach ( id => set . add ( id ) ) ;
140+ arm ( ) ;
141+ }
142+
85143export function queueFullRefresh ( ) {
144+ if ( ! fullRefreshPending && recorder . isRecording ( ) ) {
145+ recorder . record ( 'refresh.queued' , {
146+ via : new Error ( ) . stack . split ( '\n' ) . slice ( 2 , 5 ) . map ( line => line . trim ( ) ) . join ( ' < ' )
147+ } ) ;
148+ }
86149 fullRefreshPending = true ;
87150 arm ( ) ;
88151}
@@ -113,6 +176,7 @@ function flush() {
113176 }
114177
115178 firstQueuedAt = 0 ;
179+ const why = { queued : fullRefreshPending , repairs : repairPending . size , attachments : false , applier : false , comments : false } ;
116180 let needFull = fullRefreshPending ;
117181 fullRefreshPending = false ;
118182 const toRepair = [ ...repairPending ] ;
@@ -121,6 +185,8 @@ function flush() {
121185 toolboxRefreshPending = false ;
122186 const dirty = new Map ( dirtyBlocksByTarget ) ;
123187 dirtyBlocksByTarget . clear ( ) ;
188+ const dirtyComments = new Map ( dirtyCommentsByTarget ) ;
189+ dirtyCommentsByTarget . clear ( ) ;
124190
125191 const previousGroup = Blockly . Events . getGroup ( ) ;
126192 Blockly . Events . setGroup ( 'yjs-remote-sync' ) ;
@@ -143,14 +209,28 @@ function flush() {
143209 if ( ! needFull && dirty . size > 0 && editingTarget ) {
144210 const dirtyIds = dirty . get ( editingTarget . id ) ;
145211 if ( dirtyIds && dirtyIds . size > 0 ) {
146- if ( attachmentsDisagree ( editingTarget , dirtyIds ) ||
147- ! applier . reconcileBlocks ( editingTarget , dirtyIds ) ) {
212+ if ( attachmentsDisagree ( editingTarget , dirtyIds ) ) {
213+ why . attachments = true ;
214+ needFull = true ;
215+ } else if ( ! applier . reconcileBlocks ( editingTarget , dirtyIds ) ) {
216+ why . applier = true ;
148217 needFull = true ;
149218 }
150219 }
151220 }
152221
222+ if ( ! needFull && dirtyComments . size > 0 && editingTarget ) {
223+ const commentIds = dirtyComments . get ( editingTarget . id ) ;
224+ if ( commentIds && commentIds . size > 0 && ! applier . reconcileComments ( editingTarget , commentIds ) ) {
225+ why . comments = true ;
226+ needFull = true ;
227+ }
228+ }
229+
153230 if ( needFull && editingTarget ) {
231+ recorder . record ( 'refresh.full' , Object . assign ( why , {
232+ dirty : editingTarget && dirty . get ( editingTarget . id ) ? [ ...dirty . get ( editingTarget . id ) ] : [ ]
233+ } ) ) ;
154234 const repairedByTarget = new Map ( ) ;
155235 vm . runtime . targets . forEach ( target => {
156236 if ( ! target . isOriginal ) return ;
@@ -187,4 +267,5 @@ export function reset() {
187267 fullRefreshPending = false ;
188268 toolboxRefreshPending = false ;
189269 dirtyBlocksByTarget . clear ( ) ;
270+ dirtyCommentsByTarget . clear ( ) ;
190271}
0 commit comments