@trioforge/anvil 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,797 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local _charm = TS.import(script, TS.getModule(script, "@rbxts", "charm").src)
4
+ local atom = _charm.atom
5
+ local computed = _charm.computed
6
+ local effect = _charm.effect
7
+ local _services = TS.import(script, TS.getModule(script, "@rbxts", "services"))
8
+ local GuiService = _services.GuiService
9
+ local UserInputService = _services.UserInputService
10
+ local _vide = TS.import(script, TS.getModule(script, "@rbxts", "vide").src)
11
+ local cleanup = _vide.cleanup
12
+ local context = _vide.context
13
+ local videEffect = _vide.effect
14
+ local read = _vide.read
15
+ local source = _vide.source
16
+ local useAtom = TS.import(script, TS.getModule(script, "@rbxts", "vide-charm").src).useAtom
17
+ local adapter = TS.import(script, script.Parent, "adapter").adapter
18
+ local _modal_state = TS.import(script, script.Parent, "modal-state")
19
+ local enter = _modal_state.enter
20
+ local hasModal = _modal_state.hasModal
21
+ local leave = _modal_state.leave
22
+ local top = _modal_state.top
23
+ local _selection_policy = TS.import(script, script.Parent, "selection-policy")
24
+ local decide = _selection_policy.decide
25
+ local emptySelection = _selection_policy.emptySelection
26
+ local ownerContext = context("Base")
27
+ local closeFallbackContext = context(false)
28
+ local state = atom({
29
+ entries = {},
30
+ })
31
+ local modalOpen = computed(function()
32
+ return hasModal(state())
33
+ end)
34
+ local topId = computed(function()
35
+ local _result = top(state())
36
+ if _result ~= nil then
37
+ _result = _result.id
38
+ end
39
+ return _result
40
+ end)
41
+ local selectedObject = atom()
42
+ local textEditing = atom(false)
43
+ local menuOpen = atom(false)
44
+ local roots = {}
45
+ local controls = {}
46
+ local serial = 0
47
+ local baseRestore
48
+ local writing = false
49
+ local generation = 0
50
+ local waiting = {}
51
+ local selectedWatch = {}
52
+ local capturedA
53
+ local runtimeOwners = {}
54
+ -- The primitives draw the hover/selection geometry; blank only kit controls so TopbarPlus keeps its artwork.
55
+ local blankSelectionImage = Instance.new("Frame")
56
+ blankSelectionImage.Name = "SelectionImage"
57
+ blankSelectionImage.BackgroundTransparency = 1
58
+ local function active(owner)
59
+ return if owner == "Base" then not modalOpen() else topId() == owner
60
+ end
61
+ local function native(owner)
62
+ local _condition = active(owner)
63
+ if _condition then
64
+ local _condition_1 = owner == "Base"
65
+ if not _condition_1 then
66
+ local _owner = owner
67
+ local _result = roots[_owner]
68
+ if _result ~= nil then
69
+ _result = _result.mode
70
+ end
71
+ _condition_1 = _result == "Native"
72
+ end
73
+ _condition = _condition_1
74
+ end
75
+ return _condition
76
+ end
77
+ local function finite(value)
78
+ return value == value and math.abs(value) < math.huge
79
+ end
80
+ local function alive(object)
81
+ if not object:FindFirstAncestorOfClass("PlayerGui") then
82
+ return false
83
+ end
84
+ local ancestor = object
85
+ while ancestor do
86
+ if ancestor:IsA("GuiObject") and not ancestor.Visible then
87
+ return false
88
+ end
89
+ if ancestor:IsA("LayerCollector") and not ancestor.Enabled then
90
+ return false
91
+ end
92
+ ancestor = ancestor.Parent
93
+ end
94
+ return true
95
+ end
96
+ local function ready(object, isRoot)
97
+ if (not isRoot and not object.Selectable) or not alive(object) then
98
+ return false
99
+ end
100
+ local ancestor = object
101
+ while ancestor do
102
+ if ancestor:IsA("GuiObject") then
103
+ local size = ancestor.AbsoluteSize
104
+ local position = ancestor.AbsolutePosition
105
+ if not finite(size.X) or not finite(size.Y) or not finite(position.X) or not finite(position.Y) or size.X < 0 or size.Y < 0 then
106
+ return false
107
+ end
108
+ if (ancestor == object or ancestor.ClipsDescendants) and (size.X < 1 or size.Y < 1) then
109
+ return false
110
+ end
111
+ end
112
+ ancestor = ancestor.Parent
113
+ end
114
+ return true
115
+ end
116
+ local function disconnect(connections)
117
+ for _, connection in connections do
118
+ connection:Disconnect()
119
+ end
120
+ end
121
+ local function watch(object, changed)
122
+ local connections = { object.AncestryChanged:Connect(changed), object:GetPropertyChangedSignal("Selectable"):Connect(changed) }
123
+ local ancestor = object
124
+ while ancestor do
125
+ if ancestor:IsA("GuiObject") then
126
+ local _arg0 = ancestor:GetPropertyChangedSignal("AbsoluteSize"):Connect(changed)
127
+ table.insert(connections, _arg0)
128
+ local _arg0_1 = ancestor:GetPropertyChangedSignal("AbsolutePosition"):Connect(changed)
129
+ table.insert(connections, _arg0_1)
130
+ local _arg0_2 = ancestor:GetPropertyChangedSignal("Visible"):Connect(changed)
131
+ table.insert(connections, _arg0_2)
132
+ local _arg0_3 = ancestor:GetPropertyChangedSignal("ClipsDescendants"):Connect(changed)
133
+ table.insert(connections, _arg0_3)
134
+ elseif ancestor:IsA("LayerCollector") then
135
+ local _arg0 = ancestor:GetPropertyChangedSignal("Enabled"):Connect(changed)
136
+ table.insert(connections, _arg0)
137
+ end
138
+ ancestor = ancestor.Parent
139
+ end
140
+ return connections
141
+ end
142
+ local function cancelRequest()
143
+ generation += 1
144
+ disconnect(waiting)
145
+ waiting = {}
146
+ end
147
+ local function writeSelection(object)
148
+ writing = true
149
+ GuiService.SelectedObject = object
150
+ selectedObject(object)
151
+ writing = false
152
+ end
153
+ local function canSelect(owner)
154
+ return native(owner) and adapter().device() == "Gamepad" and not menuOpen() and not textEditing()
155
+ end
156
+ local function request(owner, target)
157
+ cancelRequest()
158
+ local token = generation
159
+ local _result
160
+ if target == "Root" then
161
+ local _result_1
162
+ if owner == "Base" then
163
+ _result_1 = nil
164
+ else
165
+ local _owner = owner
166
+ local _result_2 = roots[_owner]
167
+ if _result_2 ~= nil then
168
+ _result_2 = _result_2.root
169
+ end
170
+ _result_1 = _result_2
171
+ end
172
+ _result = _result_1
173
+ else
174
+ _result = target
175
+ end
176
+ local object = _result
177
+ if not object then
178
+ return nil
179
+ end
180
+ local queued = false
181
+ local queue
182
+ local retry = function()
183
+ if token ~= generation then
184
+ return nil
185
+ end
186
+ queued = false
187
+ disconnect(waiting)
188
+ waiting = {}
189
+ if not canSelect(owner) then
190
+ return nil
191
+ end
192
+ local _condition = target ~= "Root"
193
+ if _condition then
194
+ local _result_1 = controls[object]
195
+ if _result_1 ~= nil then
196
+ _result_1 = _result_1.owner
197
+ end
198
+ _condition = _result_1 ~= owner
199
+ end
200
+ if _condition then
201
+ return nil
202
+ end
203
+ if ready(object, target == "Root") then
204
+ if target == "Root" then
205
+ writing = true
206
+ GuiService:Select(object)
207
+ selectedObject(GuiService.SelectedObject)
208
+ writing = false
209
+ else
210
+ writeSelection(object)
211
+ end
212
+ else
213
+ waiting = watch(object, queue)
214
+ end
215
+ end
216
+ queue = function()
217
+ if not queued then
218
+ queued = true
219
+ task.defer(retry)
220
+ end
221
+ end
222
+ queue()
223
+ end
224
+ local function dispatch(id, event)
225
+ local _id = id
226
+ local record = roots[_id]
227
+ if not record then
228
+ return nil
229
+ end
230
+ local current = GuiService.SelectedObject
231
+ local _exp = record.selection
232
+ local _exp_1 = event
233
+ local _object = {}
234
+ local _left = "current"
235
+ local _condition = current
236
+ if _condition then
237
+ local _result = controls[current]
238
+ if _result ~= nil then
239
+ _result = _result.owner
240
+ end
241
+ _condition = _result == id
242
+ end
243
+ _object[_left] = if _condition then current else nil
244
+ _object.close = record.close
245
+ _object.alive = function(object)
246
+ local _object_1 = object
247
+ local _result = controls[_object_1]
248
+ if _result ~= nil then
249
+ _result = _result.owner
250
+ end
251
+ local _condition_1 = _result == id
252
+ if _condition_1 then
253
+ _condition_1 = alive(object)
254
+ end
255
+ return _condition_1
256
+ end
257
+ _object.canSelect = canSelect(id)
258
+ local decision = decide(_exp, _exp_1, _object)
259
+ record.selection = decision.selection
260
+ if decision.target ~= nil then
261
+ request(id, decision.target)
262
+ end
263
+ end
264
+ local function cancelA()
265
+ local session = capturedA
266
+ capturedA = nil
267
+ local _result = session
268
+ if _result ~= nil then
269
+ _result = _result.control.buttonAUp
270
+ if _result ~= nil then
271
+ _result(session.input)
272
+ end
273
+ end
274
+ end
275
+ local function selectionChanged()
276
+ local previous = selectedObject()
277
+ local current = GuiService.SelectedObject
278
+ selectedObject(current)
279
+ disconnect(selectedWatch)
280
+ selectedWatch = {}
281
+ if capturedA and capturedA.object ~= current then
282
+ cancelA()
283
+ end
284
+ if not writing then
285
+ cancelRequest()
286
+ end
287
+ local control = current and controls[current]
288
+ local _condition = current and control
289
+ if _condition then
290
+ local _owner = control.owner
291
+ _condition = type(_owner) == "number"
292
+ end
293
+ if _condition then
294
+ dispatch(control.owner, {
295
+ kind = "Selected",
296
+ object = current,
297
+ })
298
+ elseif not writing then
299
+ local previousControl = previous and controls[previous]
300
+ local _owner = previousControl
301
+ if _owner ~= nil then
302
+ _owner = _owner.owner
303
+ end
304
+ local owner = _owner
305
+ local id = topId()
306
+ if previous and type(owner) == "number" and active(owner) then
307
+ dispatch(owner, {
308
+ kind = "Lost",
309
+ removed = not alive(previous),
310
+ })
311
+ elseif id ~= nil and ((current and not control) or (previous and not previousControl)) then
312
+ dispatch(id, {
313
+ kind = "Lost",
314
+ removed = false,
315
+ })
316
+ end
317
+ end
318
+ if current and control then
319
+ selectedWatch = watch(current, function()
320
+ if GuiService.SelectedObject ~= current or not active(control.owner) or alive(current) then
321
+ return nil
322
+ end
323
+ writeSelection()
324
+ local _owner = control.owner
325
+ if type(_owner) == "number" then
326
+ dispatch(control.owner, {
327
+ kind = "Lost",
328
+ removed = true,
329
+ })
330
+ end
331
+ end)
332
+ end
333
+ end
334
+ local function openRoot(mode, onClose)
335
+ serial += 1
336
+ local id = serial
337
+ if not modalOpen() then
338
+ local selected = GuiService.SelectedObject
339
+ local _condition = selected
340
+ if _condition then
341
+ local _result = controls[selected]
342
+ if _result ~= nil then
343
+ _result = _result.owner
344
+ end
345
+ _condition = _result == "Base"
346
+ end
347
+ baseRestore = if _condition then selected else nil
348
+ end
349
+ local _arg1 = {
350
+ id = id,
351
+ serial = id,
352
+ mode = mode,
353
+ onClose = onClose,
354
+ selection = emptySelection(),
355
+ }
356
+ roots[id] = _arg1
357
+ state(enter(state(), {
358
+ id = id,
359
+ serial = id,
360
+ order = nil,
361
+ }))
362
+ return id
363
+ end
364
+ local function attachRoot(id, root)
365
+ local _id = id
366
+ local record = roots[_id]
367
+ if not record then
368
+ return function() end
369
+ end
370
+ local _result = record.stopAttachment
371
+ if _result ~= nil then
372
+ _result()
373
+ end
374
+ record.root = root
375
+ local orderConnection
376
+ local update
377
+ update = function()
378
+ local _id_1 = id
379
+ if roots[_id_1] ~= record then
380
+ return nil
381
+ end
382
+ local _result_1 = orderConnection
383
+ if _result_1 ~= nil then
384
+ _result_1:Disconnect()
385
+ end
386
+ local gui = root:FindFirstAncestorWhichIsA("ScreenGui")
387
+ if gui then
388
+ orderConnection = gui:GetPropertyChangedSignal("DisplayOrder"):Connect(update)
389
+ end
390
+ local _exp = state()
391
+ local _object = {
392
+ id = id,
393
+ serial = record.serial,
394
+ }
395
+ local _left = "order"
396
+ local _result_2 = gui
397
+ if _result_2 ~= nil then
398
+ _result_2 = _result_2.DisplayOrder
399
+ end
400
+ _object[_left] = _result_2
401
+ state(enter(_exp, _object))
402
+ end
403
+ local connection = root.AncestryChanged:Connect(update)
404
+ update()
405
+ local detach = function()
406
+ connection:Disconnect()
407
+ local _result_1 = orderConnection
408
+ if _result_1 ~= nil then
409
+ _result_1:Disconnect()
410
+ end
411
+ end
412
+ record.stopAttachment = detach
413
+ return detach
414
+ end
415
+ local function closeRoot(id)
416
+ local _id = id
417
+ local record = roots[_id]
418
+ if not record then
419
+ return nil
420
+ end
421
+ local _result = record.stopAttachment
422
+ if _result ~= nil then
423
+ _result()
424
+ end
425
+ local wasTop = topId() == id or topId() == nil
426
+ local selected = GuiService.SelectedObject
427
+ local _condition = selected
428
+ if _condition then
429
+ local _result_1 = controls[selected]
430
+ if _result_1 ~= nil then
431
+ _result_1 = _result_1.owner
432
+ end
433
+ _condition = _result_1
434
+ end
435
+ local selectedOwner = _condition
436
+ local restore = wasTop and not record.selection.paused and (selected == nil or selectedOwner == id)
437
+ if selectedOwner == id then
438
+ writeSelection()
439
+ end
440
+ local _id_1 = id
441
+ roots[_id_1] = nil
442
+ state(leave(state(), id))
443
+ if not modalOpen() then
444
+ local target = baseRestore
445
+ baseRestore = nil
446
+ local _condition_1 = restore and target
447
+ if _condition_1 then
448
+ local _result_1 = controls[target]
449
+ if _result_1 ~= nil then
450
+ _result_1 = _result_1.owner
451
+ end
452
+ _condition_1 = _result_1 == "Base"
453
+ end
454
+ if _condition_1 then
455
+ request("Base", target)
456
+ end
457
+ end
458
+ end
459
+ local function registerControl(object, control, fallback)
460
+ if fallback == nil then
461
+ fallback = false
462
+ end
463
+ local _object = object
464
+ local _control = control
465
+ controls[_object] = _control
466
+ object.SelectionImageObject = blankSelectionImage
467
+ local _owner = control.owner
468
+ local _result
469
+ if type(_owner) == "number" then
470
+ local _owner_1 = control.owner
471
+ _result = roots[_owner_1]
472
+ else
473
+ _result = nil
474
+ end
475
+ local record = _result
476
+ if fallback and record then
477
+ record.close = object
478
+ end
479
+ return function()
480
+ local selected = GuiService.SelectedObject == object
481
+ -- Vide builds a replacement before disposing the selected control; clear before Roblox chooses a neighbour.
482
+ if selected then
483
+ writeSelection()
484
+ end
485
+ local _result_1 = capturedA
486
+ if _result_1 ~= nil then
487
+ _result_1 = _result_1.object
488
+ end
489
+ if _result_1 == object then
490
+ cancelA()
491
+ end
492
+ local _object_1 = object
493
+ controls[_object_1] = nil
494
+ local _result_2 = record
495
+ if _result_2 ~= nil then
496
+ _result_2 = _result_2.close
497
+ end
498
+ if _result_2 == object then
499
+ record.close = nil
500
+ end
501
+ local _owner_1 = control.owner
502
+ if type(_owner_1) == "number" then
503
+ dispatch(control.owner, {
504
+ kind = "Unmarked",
505
+ object = object,
506
+ })
507
+ local owner = control.owner
508
+ if selected then
509
+ task.defer(function()
510
+ return dispatch(owner, {
511
+ kind = "Lost",
512
+ removed = true,
513
+ })
514
+ end)
515
+ end
516
+ end
517
+ end
518
+ end
519
+ local function markControl(owner, object, marked)
520
+ local _owner = owner
521
+ if not (type(_owner) == "number") then
522
+ return nil
523
+ end
524
+ local _owner_1 = owner
525
+ local record = roots[_owner_1]
526
+ local current = GuiService.SelectedObject
527
+ local _condition = current
528
+ if _condition then
529
+ local _result = record
530
+ if _result ~= nil then
531
+ _result = table.find(_result.selection.marked, current) ~= nil
532
+ end
533
+ _condition = _result
534
+ end
535
+ local replaces = if _condition then current else nil
536
+ dispatch(owner, if marked then {
537
+ kind = "Marked",
538
+ object = object,
539
+ replaces = replaces,
540
+ } else {
541
+ kind = "Unmarked",
542
+ object = object,
543
+ })
544
+ end
545
+ local function useSelection(options)
546
+ if options == nil then
547
+ options = {}
548
+ end
549
+ local owner = ownerContext()
550
+ local fallback = closeFallbackContext()
551
+ local object = source()
552
+ local selected = useAtom(selectedObject)
553
+ local selectable = useAtom(function()
554
+ return native(owner)
555
+ end)
556
+ local allowed = useAtom(function()
557
+ return active(owner)
558
+ end)
559
+ videEffect(function()
560
+ local instance = object()
561
+ if instance then
562
+ instance.Selectable = selectable()
563
+ end
564
+ end)
565
+ videEffect(function()
566
+ local instance = object()
567
+ if not instance then
568
+ return nil
569
+ end
570
+ local _condition = options.selectFirst
571
+ if _condition == nil then
572
+ _condition = false
573
+ end
574
+ markControl(owner, instance, read(_condition))
575
+ cleanup(function()
576
+ return markControl(owner, instance, false)
577
+ end)
578
+ end)
579
+ return {
580
+ allowed = allowed,
581
+ selected = function()
582
+ return object() ~= nil and selected() == object()
583
+ end,
584
+ action = function(instance)
585
+ local unregister = registerControl(instance, {
586
+ owner = owner,
587
+ buttonADown = options.buttonADown,
588
+ buttonAUp = options.buttonAUp,
589
+ }, fallback)
590
+ object(instance)
591
+ cleanup(unregister)
592
+ end,
593
+ }
594
+ end
595
+ local start, stop
596
+ local function startMenuRuntime()
597
+ local host = adapter()
598
+ local token = {}
599
+ runtimeOwners[token] = true
600
+ -- ▼ ReadonlySet.size ▼
601
+ local _size = 0
602
+ for _ in runtimeOwners do
603
+ _size += 1
604
+ end
605
+ -- ▲ ReadonlySet.size ▲
606
+ if _size == 1 then
607
+ start(host)
608
+ end
609
+ return function()
610
+ -- ▼ Set.delete ▼
611
+ local _valueExisted = runtimeOwners[token] ~= nil
612
+ runtimeOwners[token] = nil
613
+ -- ▲ Set.delete ▲
614
+ local _condition = _valueExisted
615
+ if _condition then
616
+ _condition = next(runtimeOwners) == nil
617
+ end
618
+ if _condition then
619
+ local _result = stop
620
+ if _result ~= nil then
621
+ _result()
622
+ end
623
+ end
624
+ end
625
+ end
626
+ function start(host)
627
+ local armed = not host.close.held()
628
+ local previousTop
629
+ local connections = { GuiService:GetPropertyChangedSignal("SelectedObject"):Connect(selectionChanged), GuiService:GetPropertyChangedSignal("MenuIsOpen"):Connect(function()
630
+ return menuOpen(GuiService.MenuIsOpen)
631
+ end), UserInputService.TextBoxFocused:Connect(function()
632
+ return textEditing(true)
633
+ end), UserInputService.TextBoxFocusReleased:Connect(function()
634
+ return task.defer(function()
635
+ return textEditing(UserInputService:GetFocusedTextBox() ~= nil)
636
+ end)
637
+ end), UserInputService.InputBegan:Connect(function(input)
638
+ if input.KeyCode ~= Enum.KeyCode.ButtonA or capturedA or textEditing() or menuOpen() then
639
+ return nil
640
+ end
641
+ local object = GuiService.SelectedObject
642
+ local control = object and controls[object]
643
+ local _condition = not object
644
+ if not _condition then
645
+ local _result = control
646
+ if _result ~= nil then
647
+ _result = _result.buttonADown
648
+ end
649
+ _condition = not _result
650
+ if not _condition then
651
+ _condition = not native(control.owner)
652
+ end
653
+ end
654
+ if _condition then
655
+ return nil
656
+ end
657
+ capturedA = {
658
+ object = object,
659
+ input = input,
660
+ control = control,
661
+ }
662
+ control.buttonADown(input)
663
+ end), UserInputService.InputEnded:Connect(function(input)
664
+ local _condition = input.KeyCode == Enum.KeyCode.ButtonA
665
+ if _condition then
666
+ local _result = capturedA
667
+ if _result ~= nil then
668
+ _result = _result.input.UserInputType
669
+ end
670
+ _condition = _result == input.UserInputType
671
+ end
672
+ if _condition then
673
+ cancelA()
674
+ end
675
+ end), UserInputService.GamepadDisconnected:Connect(function(gamepad)
676
+ local _result = capturedA
677
+ if _result ~= nil then
678
+ _result = _result.input.UserInputType
679
+ end
680
+ if _result == gamepad then
681
+ cancelA()
682
+ end
683
+ armed = not host.close.held()
684
+ end) }
685
+ local stopClose = { host.close.onPressed(function()
686
+ local id = topId()
687
+ local record = if id ~= nil then roots[id] else nil
688
+ if not armed or not record or record.mode ~= "Native" or textEditing() or menuOpen() then
689
+ return nil
690
+ end
691
+ armed = false
692
+ local _result = record.onClose
693
+ if _result ~= nil then
694
+ _result()
695
+ end
696
+ end), host.close.onReleased(function()
697
+ armed = not host.close.held()
698
+ end) }
699
+ menuOpen(GuiService.MenuIsOpen)
700
+ textEditing(UserInputService:GetFocusedTextBox() ~= nil)
701
+ selectionChanged()
702
+ local stopEffects = { effect(function()
703
+ local id = topId()
704
+ cancelRequest()
705
+ cancelA()
706
+ armed = not host.close.held()
707
+ local old = previousTop
708
+ previousTop = id
709
+ if id ~= nil and id ~= old then
710
+ task.defer(function()
711
+ if topId() == id then
712
+ dispatch(id, {
713
+ kind = "Shown",
714
+ })
715
+ end
716
+ end)
717
+ end
718
+ end), effect(function()
719
+ local id = topId()
720
+ local editing = textEditing()
721
+ local menu = menuOpen()
722
+ armed = not host.close.held()
723
+ if editing or menu then
724
+ cancelRequest()
725
+ cancelA()
726
+ end
727
+ local _condition = id ~= nil
728
+ if _condition then
729
+ local _result = roots[id]
730
+ if _result ~= nil then
731
+ _result = _result.mode
732
+ end
733
+ _condition = _result == "Native"
734
+ if _condition then
735
+ _condition = not editing
736
+ end
737
+ end
738
+ if _condition then
739
+ if not menu and GuiService.SelectedObject == nil then
740
+ task.defer(function()
741
+ if topId() == id then
742
+ dispatch(id, {
743
+ kind = "Shown",
744
+ })
745
+ end
746
+ end)
747
+ end
748
+ return host.claimMenuInput()
749
+ end
750
+ end), effect(function()
751
+ if host.device() ~= "Gamepad" then
752
+ cancelRequest()
753
+ cancelA()
754
+ return nil
755
+ end
756
+ local id = topId()
757
+ if id ~= nil and GuiService.SelectedObject == nil then
758
+ task.defer(function()
759
+ if topId() == id then
760
+ dispatch(id, {
761
+ kind = "Shown",
762
+ })
763
+ end
764
+ end)
765
+ end
766
+ end) }
767
+ stop = function()
768
+ disconnect(connections)
769
+ for _, dispose in stopClose do
770
+ dispose()
771
+ end
772
+ for _, cleanupEffect in stopEffects do
773
+ cleanupEffect()
774
+ end
775
+ cancelA()
776
+ cancelRequest()
777
+ disconnect(selectedWatch)
778
+ selectedWatch = {}
779
+ stop = nil
780
+ end
781
+ end
782
+ return {
783
+ active = active,
784
+ openRoot = openRoot,
785
+ attachRoot = attachRoot,
786
+ closeRoot = closeRoot,
787
+ registerControl = registerControl,
788
+ markControl = markControl,
789
+ useSelection = useSelection,
790
+ startMenuRuntime = startMenuRuntime,
791
+ ownerContext = ownerContext,
792
+ closeFallbackContext = closeFallbackContext,
793
+ modalOpen = modalOpen,
794
+ selectedObject = selectedObject,
795
+ textEditing = textEditing,
796
+ menuOpen = menuOpen,
797
+ }