Fix Markdown horizontal scrolling
This commit is contained in:
1 parent
b1b6647a8c
commit
d15a36d98b
6 files changed
+1373
-110
No files matched your search
File diff suppressed because it is too large.
Load diff
@@ -609,6 +609,10 @@ struct ComposerView: View {
|
||||
TextEditor(text: draft)
|
||||
.font(.body)
|
||||
.scrollContentBackground(.hidden)
|
||||
.scrollIndicators(
|
||||
draftHeight > maximumDraftHeight ? .automatic : .hidden,
|
||||
axes: .vertical
|
||||
)
|
||||
.focused($isFocused)
|
||||
.overlay(alignment: .topLeading) {
|
||||
if draft.wrappedValue.isEmpty, !hasMarkedText {
|
||||
|
||||
@@ -1847,11 +1847,17 @@ private final class NativeMarkdownView: NSView {
|
||||
}
|
||||
|
||||
@MainActor
|
||||
private final class NativeMarkdownSegment: NSView {
|
||||
final class NativeMarkdownSegment: NSView {
|
||||
private let markdown = MarkdownTextView()
|
||||
private var content: MarkdownContent?
|
||||
private var measuredSize = NSSize(width: 1, height: 1)
|
||||
private var heightConstraint: NSLayoutConstraint!
|
||||
private var horizontalScrollViews: [NSScrollView] = []
|
||||
private var horizontalTrackingAreas: [ObjectIdentifier: NSTrackingArea] = [:]
|
||||
private var horizontalScrollerVisibility: [ObjectIdentifier: Bool] = [:]
|
||||
private var horizontalScrollerAnimationGenerations: [ObjectIdentifier: Int] = [:]
|
||||
private var pendingHorizontalScrollerFadeOuts: [ObjectIdentifier: Int] = [:]
|
||||
private weak var hoveredHorizontalScrollView: NSScrollView?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
@@ -1912,12 +1918,235 @@ private final class NativeMarkdownSegment: NSView {
|
||||
let measuredHeight = max(1, ceil(markdown.boundingSize(for: measuredWidth).height))
|
||||
measuredSize = NSSize(width: measuredWidth, height: measuredHeight)
|
||||
heightConstraint.constant = measuredHeight
|
||||
markdown.layoutSubtreeIfNeeded()
|
||||
synchronizeHorizontalScrollViews()
|
||||
invalidateIntrinsicContentSize()
|
||||
}
|
||||
|
||||
override func layout() {
|
||||
super.layout()
|
||||
markdown.layoutSubtreeIfNeeded()
|
||||
synchronizeHorizontalScrollViews()
|
||||
}
|
||||
|
||||
override func viewDidMoveToWindow() {
|
||||
super.viewDidMoveToWindow()
|
||||
if window == nil { setHoveredHorizontalScrollView(nil, animated: false) }
|
||||
}
|
||||
|
||||
override func mouseEntered(with event: NSEvent) {
|
||||
guard let scrollView = horizontalScrollView(for: event.trackingArea) else {
|
||||
super.mouseEntered(with: event)
|
||||
return
|
||||
}
|
||||
setHoveredHorizontalScrollView(scrollView)
|
||||
}
|
||||
|
||||
override func mouseExited(with event: NSEvent) {
|
||||
guard let scrollView = horizontalScrollView(for: event.trackingArea) else {
|
||||
super.mouseExited(with: event)
|
||||
return
|
||||
}
|
||||
if hoveredHorizontalScrollView === scrollView {
|
||||
setHoveredHorizontalScrollView(nil)
|
||||
}
|
||||
}
|
||||
|
||||
override func hitTest(_ point: NSPoint) -> NSView? {
|
||||
let localPoint = superview.map { convert(point, from: $0) } ?? point
|
||||
if let scroller = visibleHorizontalScroller(at: localPoint) {
|
||||
return scroller
|
||||
}
|
||||
return super.hitTest(point)
|
||||
}
|
||||
|
||||
override var intrinsicContentSize: NSSize {
|
||||
measuredSize
|
||||
}
|
||||
|
||||
func updateHorizontalScrollerHover(at point: NSPoint?) {
|
||||
setHoveredHorizontalScrollView(point.flatMap(horizontalScrollView(at:)))
|
||||
}
|
||||
|
||||
private func setHoveredHorizontalScrollView(
|
||||
_ hovered: NSScrollView?,
|
||||
animated: Bool = true
|
||||
) {
|
||||
hoveredHorizontalScrollView = hovered
|
||||
for scrollView in horizontalScrollViews {
|
||||
let visible = horizontalOverflow(in: scrollView) && scrollView === hovered
|
||||
updateHorizontalScrollerVisibility(
|
||||
visible,
|
||||
in: scrollView,
|
||||
animated: animated && window != nil
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private func updateHorizontalScrollerVisibility(
|
||||
_ visible: Bool,
|
||||
in scrollView: NSScrollView,
|
||||
animated: Bool
|
||||
) {
|
||||
guard let scroller = scrollView.horizontalScroller else { return }
|
||||
let identifier = ObjectIdentifier(scrollView)
|
||||
guard horizontalScrollerVisibility[identifier] != visible else {
|
||||
if visible, scroller.isHidden {
|
||||
scroller.isHidden = false
|
||||
scroller.alphaValue = 1
|
||||
} else if !visible, pendingHorizontalScrollerFadeOuts[identifier] == nil {
|
||||
scroller.alphaValue = 0
|
||||
scroller.isHidden = true
|
||||
}
|
||||
return
|
||||
}
|
||||
horizontalScrollerVisibility[identifier] = visible
|
||||
let generation = horizontalScrollerAnimationGenerations[identifier, default: 0] + 1
|
||||
horizontalScrollerAnimationGenerations[identifier] = generation
|
||||
|
||||
if visible {
|
||||
pendingHorizontalScrollerFadeOuts.removeValue(forKey: identifier)
|
||||
scroller.isHidden = false
|
||||
}
|
||||
guard animated else {
|
||||
pendingHorizontalScrollerFadeOuts.removeValue(forKey: identifier)
|
||||
scroller.alphaValue = visible ? 1 : 0
|
||||
scroller.isHidden = !visible
|
||||
return
|
||||
}
|
||||
|
||||
if visible {
|
||||
NSAnimationContext.runAnimationGroup { context in
|
||||
context.duration = 0.12
|
||||
context.allowsImplicitAnimation = true
|
||||
scroller.animator().alphaValue = 1
|
||||
}
|
||||
} else {
|
||||
pendingHorizontalScrollerFadeOuts[identifier] = generation
|
||||
NSAnimationContext.runAnimationGroup { context in
|
||||
context.duration = 0.16
|
||||
context.allowsImplicitAnimation = true
|
||||
scroller.animator().alphaValue = 0
|
||||
} completionHandler: { [weak self, weak scroller] in
|
||||
Task { @MainActor in
|
||||
guard let self, let scroller,
|
||||
self.horizontalScrollerVisibility[identifier] == false,
|
||||
self.pendingHorizontalScrollerFadeOuts[identifier] == generation
|
||||
else { return }
|
||||
self.pendingHorizontalScrollerFadeOuts.removeValue(forKey: identifier)
|
||||
scroller.isHidden = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func synchronizeHorizontalScrollViews() {
|
||||
let previous = horizontalScrollViews
|
||||
let candidates = descendantScrollViews(in: markdown).filter {
|
||||
!$0.hasVerticalScroller && $0.documentView != nil
|
||||
}
|
||||
horizontalScrollViews = candidates
|
||||
|
||||
for scrollView in previous where !candidates.contains(where: { $0 === scrollView }) {
|
||||
removeHorizontalTrackingArea(from: scrollView)
|
||||
updateHorizontalScrollerVisibility(false, in: scrollView, animated: false)
|
||||
horizontalScrollerVisibility.removeValue(forKey: ObjectIdentifier(scrollView))
|
||||
horizontalScrollerAnimationGenerations.removeValue(forKey: ObjectIdentifier(scrollView))
|
||||
pendingHorizontalScrollerFadeOuts.removeValue(forKey: ObjectIdentifier(scrollView))
|
||||
}
|
||||
|
||||
for scrollView in candidates {
|
||||
scrollView.scrollerStyle = .overlay
|
||||
scrollView.hasHorizontalScroller = true
|
||||
scrollView.autohidesScrollers = false
|
||||
scrollView.horizontalScroller?.controlSize = .mini
|
||||
scrollView.tile()
|
||||
|
||||
let overflows = horizontalOverflow(in: scrollView)
|
||||
let scroller = scrollView.horizontalScroller
|
||||
scroller?.scrollerStyle = .legacy
|
||||
scroller?.isEnabled = overflows
|
||||
if overflows {
|
||||
installHorizontalTrackingAreaIfNeeded(on: scrollView)
|
||||
if horizontalScrollerVisibility[ObjectIdentifier(scrollView)] == nil {
|
||||
updateHorizontalScrollerVisibility(false, in: scrollView, animated: false)
|
||||
}
|
||||
} else {
|
||||
removeHorizontalTrackingArea(from: scrollView)
|
||||
updateHorizontalScrollerVisibility(false, in: scrollView, animated: false)
|
||||
}
|
||||
}
|
||||
|
||||
if let hoveredHorizontalScrollView,
|
||||
!candidates.contains(where: { $0 === hoveredHorizontalScrollView })
|
||||
|| !horizontalOverflow(in: hoveredHorizontalScrollView)
|
||||
{
|
||||
self.hoveredHorizontalScrollView = nil
|
||||
}
|
||||
setHoveredHorizontalScrollView(hoveredHorizontalScrollView, animated: false)
|
||||
}
|
||||
|
||||
private func installHorizontalTrackingAreaIfNeeded(on scrollView: NSScrollView) {
|
||||
let identifier = ObjectIdentifier(scrollView)
|
||||
guard horizontalTrackingAreas[identifier] == nil else { return }
|
||||
let area = NSTrackingArea(
|
||||
rect: .zero,
|
||||
options: [.activeInKeyWindow, .inVisibleRect, .mouseEnteredAndExited],
|
||||
owner: self,
|
||||
userInfo: nil
|
||||
)
|
||||
scrollView.addTrackingArea(area)
|
||||
horizontalTrackingAreas[identifier] = area
|
||||
}
|
||||
|
||||
private func removeHorizontalTrackingArea(from scrollView: NSScrollView) {
|
||||
guard let area = horizontalTrackingAreas.removeValue(forKey: ObjectIdentifier(scrollView)) else {
|
||||
return
|
||||
}
|
||||
scrollView.removeTrackingArea(area)
|
||||
}
|
||||
|
||||
private func horizontalScrollView(for trackingArea: NSTrackingArea?) -> NSScrollView? {
|
||||
guard let trackingArea else { return nil }
|
||||
return horizontalScrollViews.first { scrollView in
|
||||
horizontalTrackingAreas[ObjectIdentifier(scrollView)] === trackingArea
|
||||
}
|
||||
}
|
||||
|
||||
private func horizontalScrollView(at point: NSPoint) -> NSScrollView? {
|
||||
horizontalScrollViews.last { scrollView in
|
||||
horizontalOverflow(in: scrollView)
|
||||
&& convert(scrollView.bounds, from: scrollView).contains(point)
|
||||
}
|
||||
}
|
||||
|
||||
private func visibleHorizontalScroller(at point: NSPoint) -> NSScroller? {
|
||||
for scrollView in horizontalScrollViews.reversed() {
|
||||
guard horizontalOverflow(in: scrollView),
|
||||
let scroller = scrollView.horizontalScroller,
|
||||
!scroller.isHidden,
|
||||
scroller.alphaValue > 0.01
|
||||
else { continue }
|
||||
let scrollerPoint = scroller.convert(point, from: self)
|
||||
if scroller.bounds.contains(scrollerPoint) { return scroller }
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
private func horizontalOverflow(in scrollView: NSScrollView) -> Bool {
|
||||
guard let documentView = scrollView.documentView else { return false }
|
||||
return documentView.frame.width > scrollView.contentView.bounds.width + 1
|
||||
}
|
||||
|
||||
private func descendantScrollViews(in root: NSView) -> [NSScrollView] {
|
||||
var result: [NSScrollView] = []
|
||||
var pending = root.subviews
|
||||
while let view = pending.popLast() {
|
||||
if let scrollView = view as? NSScrollView { result.append(scrollView) }
|
||||
pending.append(contentsOf: view.subviews)
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
|
||||
@@ -1118,6 +1118,145 @@ struct TranscriptListViewBridgeTests {
|
||||
#expect(actions.alphaValue == 0)
|
||||
}
|
||||
|
||||
@Test
|
||||
func overflowingMarkdownBlocksExposeHoverableHorizontalScrollers() throws {
|
||||
let coordinator = TranscriptListView.Coordinator()
|
||||
let listView = coordinator.makeListView()
|
||||
let id = UUID()
|
||||
listView.frame = CGRect(x: 0, y: 0, width: 320, height: 800)
|
||||
let longValue = String(repeating: "wide-content-", count: 24)
|
||||
let markdown = """
|
||||
| Name | Value |
|
||||
| --- | --- |
|
||||
| table | \(longValue) |
|
||||
|
||||
```swift
|
||||
let value = "\(longValue)"
|
||||
```
|
||||
"""
|
||||
|
||||
update(coordinator, listView, entries: [assistant(id: id, text: markdown)])
|
||||
listView.layoutSubtreeIfNeeded()
|
||||
|
||||
let row = try #require(listView.rowView(for: id))
|
||||
let segment = try #require(descendant(in: row, ofType: NativeMarkdownSegment.self))
|
||||
segment.layoutSubtreeIfNeeded()
|
||||
let scrollViews = horizontalScrollViews(in: segment)
|
||||
let allHaveHorizontalScrollers = scrollViews.allSatisfy { $0.hasHorizontalScroller }
|
||||
let allScrollersAreHidden = scrollViews.allSatisfy {
|
||||
$0.horizontalScroller?.alphaValue == 0
|
||||
&& $0.horizontalScroller?.isHidden == true
|
||||
}
|
||||
#expect(scrollViews.count == 2)
|
||||
#expect(allHaveHorizontalScrollers)
|
||||
#expect(allScrollersAreHidden)
|
||||
#expect(segment.trackingAreas.allSatisfy { !$0.options.contains(.mouseMoved) })
|
||||
|
||||
let trackingAreas = try scrollViews.map { scrollView in
|
||||
try #require(
|
||||
scrollView.trackingAreas.first { area in
|
||||
area.owner === segment
|
||||
&& area.options.contains(.inVisibleRect)
|
||||
&& area.options.contains(.mouseEnteredAndExited)
|
||||
&& !area.options.contains(.mouseMoved)
|
||||
}
|
||||
)
|
||||
}
|
||||
segment.needsLayout = true
|
||||
segment.layoutSubtreeIfNeeded()
|
||||
for (scrollView, trackingArea) in zip(scrollViews, trackingAreas) {
|
||||
let ownedTrackingAreas = scrollView.trackingAreas.filter { $0.owner === segment }
|
||||
#expect(ownedTrackingAreas.count == 1)
|
||||
#expect(ownedTrackingAreas.first === trackingArea)
|
||||
}
|
||||
|
||||
for scrollView in scrollViews {
|
||||
let scroller = try #require(scrollView.horizontalScroller)
|
||||
let contentHit = [
|
||||
CGPoint(x: scrollView.bounds.midX, y: scrollView.bounds.minY + 2),
|
||||
CGPoint(x: scrollView.bounds.midX, y: scrollView.bounds.maxY - 2),
|
||||
CGPoint(x: scrollView.bounds.midX, y: scrollView.bounds.midY),
|
||||
].lazy.compactMap { point -> (NSPoint, NSView)? in
|
||||
guard !scroller.frame.contains(point) else { return nil }
|
||||
let localPoint = segment.convert(point, from: scrollView)
|
||||
guard let superview = segment.superview else { return nil }
|
||||
let hitPoint = superview.convert(localPoint, from: segment)
|
||||
guard let hit = segment.hitTest(hitPoint), !(hit is NSScroller) else { return nil }
|
||||
return (hitPoint, hit)
|
||||
}.first
|
||||
|
||||
let hoverPoint = segment.convert(
|
||||
CGPoint(x: scrollView.bounds.midX, y: scrollView.bounds.midY),
|
||||
from: scrollView
|
||||
)
|
||||
segment.updateHorizontalScrollerHover(at: hoverPoint)
|
||||
|
||||
#expect(scroller.alphaValue == 1)
|
||||
#expect(!scroller.isHidden)
|
||||
#expect(scroller.scrollerStyle == .legacy)
|
||||
#expect(scroller.controlSize == .mini)
|
||||
#expect(scroller.frame.width > 0)
|
||||
#expect(scroller.frame.height > 0)
|
||||
#expect(
|
||||
scroller.frame.height
|
||||
<= NSScroller.scrollerWidth(for: .mini, scrollerStyle: .legacy) + 1
|
||||
)
|
||||
if let (hitPoint, hitBeforeHover) = contentHit {
|
||||
#expect(segment.hitTest(hitPoint) === hitBeforeHover)
|
||||
}
|
||||
|
||||
let localScrollerPoint = segment.convert(
|
||||
CGPoint(x: scroller.bounds.midX, y: scroller.bounds.midY),
|
||||
from: scroller
|
||||
)
|
||||
let scrollerHitPoint = try #require(segment.superview).convert(localScrollerPoint, from: segment)
|
||||
#expect(segment.hitTest(scrollerHitPoint) === scroller)
|
||||
|
||||
let documentView = try #require(scrollView.documentView)
|
||||
let maximumX = documentView.frame.width - scrollView.contentView.bounds.width
|
||||
scrollView.contentView.scroll(to: CGPoint(x: maximumX, y: 0))
|
||||
scrollView.reflectScrolledClipView(scrollView.contentView)
|
||||
#expect(scrollView.contentView.bounds.minX > 0)
|
||||
}
|
||||
|
||||
segment.updateHorizontalScrollerHover(at: nil)
|
||||
#expect(scrollViews.allSatisfy { $0.horizontalScroller?.alphaValue == 0 })
|
||||
#expect(scrollViews.allSatisfy { $0.horizontalScroller?.isHidden == true })
|
||||
}
|
||||
|
||||
@Test
|
||||
func markdownScrollersStayHiddenWhenTheirContentFits() throws {
|
||||
let coordinator = TranscriptListView.Coordinator()
|
||||
let listView = coordinator.makeListView()
|
||||
let id = UUID()
|
||||
listView.frame = CGRect(x: 0, y: 0, width: 900, height: 800)
|
||||
let markdown = """
|
||||
| Name | Value |
|
||||
| --- | --- |
|
||||
| table | short |
|
||||
|
||||
```swift
|
||||
let value = 1
|
||||
```
|
||||
"""
|
||||
|
||||
update(coordinator, listView, entries: [assistant(id: id, text: markdown)])
|
||||
listView.layoutSubtreeIfNeeded()
|
||||
|
||||
let row = try #require(listView.rowView(for: id))
|
||||
let segment = try #require(descendant(in: row, ofType: NativeMarkdownSegment.self))
|
||||
segment.layoutSubtreeIfNeeded()
|
||||
let scrollViews = descendants(in: segment, ofType: NSScrollView.self)
|
||||
#expect(scrollViews.count == 2)
|
||||
|
||||
segment.updateHorizontalScrollerHover(
|
||||
at: CGPoint(x: segment.bounds.midX, y: segment.bounds.midY)
|
||||
)
|
||||
#expect(scrollViews.allSatisfy { $0.horizontalScroller?.alphaValue == 0 })
|
||||
#expect(scrollViews.allSatisfy { $0.horizontalScroller?.isHidden == true })
|
||||
#expect(scrollViews.allSatisfy { $0.horizontalScroller?.isEnabled == false })
|
||||
}
|
||||
|
||||
@Test
|
||||
func disclosureRefreshRebuildsTheVisiblePlanAndItsHeight() {
|
||||
let coordinator = TranscriptListView.Coordinator()
|
||||
@@ -1349,6 +1488,28 @@ struct TranscriptListViewBridgeTests {
|
||||
return nil
|
||||
}
|
||||
|
||||
private func descendant<View: NSView>(in root: NSView, ofType _: View.Type) -> View? {
|
||||
descendants(in: root, ofType: View.self).first
|
||||
}
|
||||
|
||||
private func descendants<View: NSView>(in root: NSView, ofType _: View.Type) -> [View] {
|
||||
var result: [View] = []
|
||||
var queue = root.subviews
|
||||
while let view = queue.first {
|
||||
queue.removeFirst()
|
||||
if let match = view as? View { result.append(match) }
|
||||
queue.append(contentsOf: view.subviews)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private func horizontalScrollViews(in root: NSView) -> [NSScrollView] {
|
||||
descendants(in: root, ofType: NSScrollView.self).filter { scrollView in
|
||||
guard let documentView = scrollView.documentView else { return false }
|
||||
return documentView.frame.width > scrollView.contentView.bounds.width + 1
|
||||
}
|
||||
}
|
||||
|
||||
private func expectProminentForeground(_ button: NSButton, component: CGFloat) throws {
|
||||
let titleColor = try #require(
|
||||
button.attributedTitle.attribute(
|
||||
|
||||
@@ -67,6 +67,7 @@
|
||||
C2D4B372BA5A040DDBCEFC85 /* TermindApplication.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 10111AE8F047FB59B318813A /* TermindApplication.framework */; };
|
||||
C4AD00000000000000000001 /* ZIPFoundation in Frameworks */ = {isa = PBXBuildFile; productRef = TRMD00000000000000100015 /* ZIPFoundation */; };
|
||||
C4AD00000000000000000002 /* MarkdownView in Frameworks */ = {isa = PBXBuildFile; productRef = TRMD00000000000000100017 /* MarkdownView */; };
|
||||
CE6200000000000000000001 /* TermindRemoteInfrastructure.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 21951230A1FF30987ADC3B6B /* TermindRemoteInfrastructure.framework */; };
|
||||
CF863393BB8C2B38A9EEE18A /* TermindInfrastructure.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 67B48098792E58C2955768F2 /* TermindInfrastructure.framework */; };
|
||||
D7D1BBB65020F61F7EFDDFD7 /* GhosttyTerminal in Frameworks */ = {isa = PBXBuildFile; productRef = TRMD00000000000000100012 /* GhosttyTerminal */; };
|
||||
E635C54CA9F1A63D909CFCE6 /* TermindPersistence.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 244632CCDB1CA43F45C0EB54 /* TermindPersistence.framework */; };
|
||||
@@ -88,7 +89,6 @@
|
||||
TRMD0000000000000010000D /* AgentKit in Frameworks */ = {isa = PBXBuildFile; productRef = TRMD0000000000000010001B /* AgentKit */; };
|
||||
TRMD0000000000000010000E /* AgentKitScrubber in Frameworks */ = {isa = PBXBuildFile; productRef = TRMD0000000000000010001C /* AgentKitScrubber */; };
|
||||
TRMD0000000000000010000F /* AgentKitScrubber in Frameworks */ = {isa = PBXBuildFile; productRef = TRMD0000000000000010001C /* AgentKitScrubber */; };
|
||||
CE6200000000000000000001 /* TermindRemoteInfrastructure.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 21951230A1FF30987ADC3B6B /* TermindRemoteInfrastructure.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -183,6 +183,13 @@
|
||||
remoteGlobalIDString = A5F3ED08556B8962920EF788;
|
||||
remoteInfo = Framework;
|
||||
};
|
||||
CE6200000000000000000002 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = TRMD00000000000000000090 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = BDA6B93652DE90AEBDA7090E;
|
||||
remoteInfo = TermindRemoteInfrastructure;
|
||||
};
|
||||
DB8AB3CC3053A07800C7EFAE /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = TRMD00000000000000000090 /* Project object */;
|
||||
@@ -386,13 +393,6 @@
|
||||
remoteGlobalIDString = TRMD00000000000000030050;
|
||||
remoteInfo = TermindMCPBridge;
|
||||
};
|
||||
CE6200000000000000000002 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = TRMD00000000000000000090 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = BDA6B93652DE90AEBDA7090E;
|
||||
remoteInfo = TermindRemoteInfrastructure;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
@@ -1603,6 +1603,11 @@
|
||||
target = A5F3ED08556B8962920EF788 /* TermindApplication */;
|
||||
targetProxy = DB8AB3DD3053A07900C7EFAE /* PBXContainerItemProxy */;
|
||||
};
|
||||
CE6200000000000000000003 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = BDA6B93652DE90AEBDA7090E /* TermindRemoteInfrastructure */;
|
||||
targetProxy = CE6200000000000000000002 /* PBXContainerItemProxy */;
|
||||
};
|
||||
D6444C208365A08727ACC139 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = B243FDB536F5111B356353DA /* TermindDomain */;
|
||||
@@ -1638,11 +1643,6 @@
|
||||
target = TRMD00000000000000030050 /* TermindMCPBridge */;
|
||||
targetProxy = TRMD00000000000000030090 /* PBXContainerItemProxy */;
|
||||
};
|
||||
CE6200000000000000000003 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = BDA6B93652DE90AEBDA7090E /* TermindRemoteInfrastructure */;
|
||||
targetProxy = CE6200000000000000000002 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "2700"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES"
|
||||
buildArchitectures = "Automatic">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "TRMD00000000000000030050"
|
||||
BuildableName = "termind-mcp"
|
||||
ReferencedContainer = "container:Termind.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES"
|
||||
viewDebuggingEnabled = "No"
|
||||
queueDebuggingEnabled = "No">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "TRMD00000000000000030050"
|
||||
BuildableName = "termind-mcp"
|
||||
ReferencedContainer = "container:Termind.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "TRMD00000000000000030050"
|
||||
BuildableName = "termind-mcp"
|
||||
ReferencedContainer = "container:Termind.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
Reference in new issue
Block a user