diff --git a/com.stakwork.sphinx.desktop/Helpers/ChatHelper.swift b/com.stakwork.sphinx.desktop/Helpers/ChatHelper.swift
index a1611c2d..4c2b1b3d 100644
--- a/com.stakwork.sphinx.desktop/Helpers/ChatHelper.swift
+++ b/com.stakwork.sphinx.desktop/Helpers/ChatHelper.swift
@@ -746,7 +746,7 @@ class ChatHelper {
return viewsHeight
}
- public static func getTextHeightFor(
+ @MainActor public static func getTextHeightFor(
text: String,
width: CGFloat,
font: NSFont? = nil,
@@ -761,7 +761,19 @@ class ChatHelper {
let attributedString: NSAttributedString
if useMarkdown {
- attributedString = ChatHelper.markdownRenderer.render(text)
+ let rendered = NSMutableAttributedString(
+ attributedString: ChatHelper.markdownRenderer.render(text)
+ )
+ // Strip .link so boundingRect applies the same slash-boundary word-breaking
+ // used by the displayed string. Intentionally uses bulk removeAttribute
+ // rather than MessageTextField.stripLinkAttributes — measurement only needs
+ // removal; .sphinxURL substitution is not needed here and coupling ChatHelper
+ // to MessageTextField for a 1-line operation adds unnecessary overhead.
+ // If stripLinkAttributes is ever changed (e.g. conditional skip logic or a
+ // different replacement attribute), audit this site to keep the two paths
+ // in sync.
+ rendered.removeAttribute(.link, range: NSRange(location: 0, length: rendered.length))
+ attributedString = rendered
} else {
let attrs = [NSAttributedString.Key.font: font ?? Constants.kMessageFont]
let mutable = NSMutableAttributedString(string: text, attributes: attrs)
@@ -827,6 +839,10 @@ class ChatHelper {
}
}
+ // Strip .link before boundingRect — the display path removes .link from URL
+ // runs, and leaving it here causes the same undercount as the useMarkdown: true
+ // path. See comment in the useMarkdown: true branch above for rationale.
+ mutable.removeAttribute(.link, range: NSRange(location: 0, length: mutable.length))
attributedString = mutable
}
diff --git a/com.stakwork.sphinx.desktopTests/ChatHelperHeightTests.swift b/com.stakwork.sphinx.desktopTests/ChatHelperHeightTests.swift
new file mode 100644
index 00000000..8c69cf9d
--- /dev/null
+++ b/com.stakwork.sphinx.desktopTests/ChatHelperHeightTests.swift
@@ -0,0 +1,31 @@
+//
+// ChatHelperHeightTests.swift
+// com.stakwork.sphinx.desktopTests
+//
+
+import XCTest
+@testable import sphinx
+
+final class ChatHelperHeightTests: XCTestCase {
+
+ @MainActor
+ func testGetTextHeightForMatchesBoundingRectAfterLinkStrip() {
+ let text = "Check this out https://hive.sphinx.chat/w/graphmindset/plan/cmt1iwce9000hjt04m755nqc5"
+ let width: CGFloat = 500
+
+ // Manually reproduce what the fixed getTextHeightFor does internally
+ let rendered = NSMutableAttributedString(
+ attributedString: ChatHelper.markdownRenderer.render(text)
+ )
+ rendered.removeAttribute(.link, range: NSRange(location: 0, length: rendered.length))
+ let expected = rendered.boundingRect(
+ with: CGSize(width: width, height: .greatestFiniteMagnitude),
+ options: [.usesLineFragmentOrigin, .usesFontLeading]
+ ).height
+
+ let actual = ChatHelper.getTextHeightFor(text: text, width: width, useMarkdown: true)
+
+ XCTAssertEqual(actual, expected, accuracy: 1.0,
+ "getTextHeightFor must match boundingRect on a .link-stripped string")
+ }
+}
diff --git a/com.stakwork.sphinx.desktopTests/Info.plist b/com.stakwork.sphinx.desktopTests/Info.plist
index 64d65ca4..11173ad1 100644
--- a/com.stakwork.sphinx.desktopTests/Info.plist
+++ b/com.stakwork.sphinx.desktopTests/Info.plist
@@ -18,5 +18,7 @@
1.0
CFBundleVersion
1
+ ATSApplicationFontsPath
+ Fonts
diff --git a/sphinx.xcodeproj/project.pbxproj b/sphinx.xcodeproj/project.pbxproj
index 81d90c6c..e622327d 100644
--- a/sphinx.xcodeproj/project.pbxproj
+++ b/sphinx.xcodeproj/project.pbxproj
@@ -750,6 +750,8 @@
PTFT00100000000001PTFT001 /* PaddedTextFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = PTFT00000000000001PTFT000 /* PaddedTextFieldTests.swift */; };
SOMCT0020000000001SOMCT02 /* SphinxOnionManagerConnectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = SOMCT0010000000001SOMCT01 /* SphinxOnionManagerConnectionTests.swift */; };
SOMSG0020000000001SOMSG02 /* SphinxOnionManagerSeedGenerationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = SOMSG0010000000001SOMSG01 /* SphinxOnionManagerSeedGenerationTests.swift */; };
+ CHHT0020000000001CHHT002 /* ChatHelperHeightTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CHHT0010000000001CHHT001 /* ChatHelperHeightTests.swift */; };
+ CHHT0030000000001CHHT003 /* Roboto-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 4703F05B2481C61B00282186 /* Roboto-Light.ttf */; };
TRTTS0020000000001TRTTS02 /* TribeTimestampTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = TRTTS0010000000001TRTTS01 /* TribeTimestampTests.swift */; };
/* End PBXBuildFile section */
@@ -1845,6 +1847,7 @@
PTFT00000000000001PTFT000 /* PaddedTextFieldTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaddedTextFieldTests.swift; sourceTree = ""; };
SOMCT0010000000001SOMCT01 /* SphinxOnionManagerConnectionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SphinxOnionManagerConnectionTests.swift; sourceTree = ""; };
SOMSG0010000000001SOMSG01 /* SphinxOnionManagerSeedGenerationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SphinxOnionManagerSeedGenerationTests.swift; sourceTree = ""; };
+ CHHT0010000000001CHHT001 /* ChatHelperHeightTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatHelperHeightTests.swift; sourceTree = ""; };
TRTTS0010000000001TRTTS01 /* TribeTimestampTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TribeTimestampTests.swift; sourceTree = ""; };
/* End PBXFileReference section */
@@ -2262,6 +2265,7 @@
TRTTS0010000000001TRTTS01 /* TribeTimestampTests.swift */,
SOMSG0010000000001SOMSG01 /* SphinxOnionManagerSeedGenerationTests.swift */,
SOMCT0010000000001SOMCT01 /* SphinxOnionManagerConnectionTests.swift */,
+ CHHT0010000000001CHHT001 /* ChatHelperHeightTests.swift */,
4734D3AD2417E3D500D6957E /* Info.plist */,
);
path = com.stakwork.sphinx.desktopTests;
@@ -4489,6 +4493,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ CHHT0030000000001CHHT003 /* Roboto-Light.ttf in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -5130,6 +5135,7 @@
TRTTS0020000000001TRTTS02 /* TribeTimestampTests.swift in Sources */,
SOMSG0020000000001SOMSG02 /* SphinxOnionManagerSeedGenerationTests.swift in Sources */,
SOMCT0020000000001SOMCT02 /* SphinxOnionManagerConnectionTests.swift in Sources */,
+ CHHT0020000000001CHHT002 /* ChatHelperHeightTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};