@@ -1164,6 +1164,9 @@ enum PinboardRealQACommand {
11641164 )
11651165 let githubAssets = try PanelQASamples . prepareRealGitHubSampleAssets ( appSupportURL: appSupportURL)
11661166 let sourceIconPaths = try PanelQASamples . makeSourceAppIconPaths ( outputDirectory: appSupportURL)
1167+ let terminalRichTextPreviewURL = try PanelQASamples . makeTerminalRichTextPreviewURL (
1168+ outputDirectory: appSupportURL
1169+ )
11671170 PanelCardAssetResolver . primePreviewImageCacheForSmoke ( paths: [
11681171 imagePreviewURL. path,
11691172 filePreviewURL. path,
@@ -1174,7 +1177,8 @@ enum PinboardRealQACommand {
11741177 imagePayloadPath: imageURL. path,
11751178 filePreviewPath: filePreviewURL. path,
11761179 linkMetadata: githubAssets. linkMetadata,
1177- sourceIconPaths: sourceIconPaths
1180+ sourceIconPaths: sourceIconPaths,
1181+ terminalRichTextPreviewPath: terminalRichTextPreviewURL. path
11781182 )
11791183 let frame = CommandLineWindowPlacement . bottomPanelFrame (
11801184 arguments: arguments,
@@ -1210,7 +1214,7 @@ enum PinboardRealQACommand {
12101214
12111215 let mode = mode ( arguments: arguments)
12121216 let targetPinboardID = mode == " ai " ? " ai " : " untitled-new "
1213- if mode == " overview " || mode == " preview " {
1217+ if mode == " overview " || mode == " preview " || mode == " verify-rich-text " {
12141218 contentView. updateListState (
12151219 . success( RustCoreListResult (
12161220 items: sampleItems,
@@ -1242,6 +1246,11 @@ enum PinboardRealQACommand {
12421246 break
12431247 }
12441248
1249+ if mode == " verify-rich-text " {
1250+ try verifyRichTextCard ( contentView: contentView)
1251+ return
1252+ }
1253+
12451254 DispatchQueue . main. asyncAfter ( deadline: . now( ) + 0.25 ) {
12461255 switch mode {
12471256 case " menu " :
@@ -1269,6 +1278,97 @@ enum PinboardRealQACommand {
12691278 CommandLineArgumentReader . value ( after: flag, in: arguments) ?? " toolbar "
12701279 }
12711280
1281+ @MainActor
1282+ private static func verifyRichTextCard( contentView: FloatingPanelContentView ) throws {
1283+ let itemID = " panel-smoke-text "
1284+ RunLoop . main. run ( until: Date ( ) . addingTimeInterval ( 0.25 ) )
1285+ contentView. layoutSubtreeIfNeeded ( )
1286+
1287+ let theme = ClipDockTheme . current ( for: contentView)
1288+ guard let card = contentView. smokeCardBoxes ( ) . first ( where: { $0. itemID == itemID } ) else {
1289+ throw CommandLineQAError ( message: " rich text QA card is not visible " )
1290+ }
1291+ guard colorAndAlphaDistance ( card. fillColor, theme. card. textItemBackgroundColor) < 0.01 else {
1292+ throw CommandLineQAError ( message: " rich text QA card fill does not use themed text surface " )
1293+ }
1294+ guard let attributed = contentView. smokeBodyAttributedStrings ( itemID: itemID) . first else {
1295+ throw CommandLineQAError ( message: " rich text QA body attributed string is missing " )
1296+ }
1297+ let gitRange = ( attributed. string as NSString ) . range ( of: " git " )
1298+ guard gitRange. location != NSNotFound else {
1299+ throw CommandLineQAError ( message: " rich text QA body does not contain terminal sample text " )
1300+ }
1301+ let expectedTerminalBackground = NSColor ( deviceWhite: 0.24 , alpha: 1 )
1302+ guard let renderedBackground = attributed. attribute (
1303+ . backgroundColor,
1304+ at: gitRange. location,
1305+ effectiveRange: nil
1306+ ) as? NSColor else {
1307+ throw CommandLineQAError ( message: " rich text QA body did not preserve terminal background " )
1308+ }
1309+ guard colorAndAlphaDistance ( renderedBackground, expectedTerminalBackground) < 0.01 else {
1310+ throw CommandLineQAError ( message: " rich text QA body terminal background changed " )
1311+ }
1312+ guard let fadeView = descendant ( in: card, identifier: " TextBodyBottomFade " ) ,
1313+ let fadeColors = fadeView as? PanelTextBodyFadeColorProviding
1314+ else {
1315+ throw CommandLineQAError ( message: " rich text QA bottom fade is missing " )
1316+ }
1317+ guard colorAndAlphaDistance (
1318+ fadeColors. smokeFadeBottomColor,
1319+ theme. card. textBodyFadeBottomColor
1320+ ) < 0.01 else {
1321+ throw CommandLineQAError ( message: " rich text QA fade does not use themed bottom color " )
1322+ }
1323+
1324+ print ( " rich_text_card_verify=passed " )
1325+ print ( " card_fill= \( rgbaDescription ( card. fillColor) ) " )
1326+ print ( " theme_text_surface= \( rgbaDescription ( theme. card. textItemBackgroundColor) ) " )
1327+ print ( " content_background= \( rgbaDescription ( renderedBackground) ) " )
1328+ print ( " expected_terminal_background= \( rgbaDescription ( expectedTerminalBackground) ) " )
1329+ print ( " fade_bottom= \( rgbaDescription ( fadeColors. smokeFadeBottomColor) ) " )
1330+ print ( " theme_fade_bottom= \( rgbaDescription ( theme. card. textBodyFadeBottomColor) ) " )
1331+ }
1332+
1333+ @MainActor
1334+ private static func descendant( in view: NSView , identifier: String ) -> NSView ? {
1335+ if view. identifier? . rawValue == identifier {
1336+ return view
1337+ }
1338+ for subview in view. subviews {
1339+ if let found = descendant ( in: subview, identifier: identifier) {
1340+ return found
1341+ }
1342+ }
1343+ return nil
1344+ }
1345+
1346+ private static func colorAndAlphaDistance( _ lhs: NSColor , _ rhs: NSColor ) -> CGFloat {
1347+ guard let lhs = lhs. usingColorSpace ( . sRGB) ,
1348+ let rhs = rhs. usingColorSpace ( . sRGB)
1349+ else {
1350+ return . greatestFiniteMagnitude
1351+ }
1352+
1353+ return abs ( lhs. redComponent - rhs. redComponent)
1354+ + abs( lhs. greenComponent - rhs. greenComponent)
1355+ + abs( lhs. blueComponent - rhs. blueComponent)
1356+ + abs( lhs. alphaComponent - rhs. alphaComponent)
1357+ }
1358+
1359+ private static func rgbaDescription( _ color: NSColor ) -> String {
1360+ guard let rgb = color. usingColorSpace ( . sRGB) else {
1361+ return " unavailable "
1362+ }
1363+ return String (
1364+ format: " %.3f,%.3f,%.3f,%.3f " ,
1365+ rgb. redComponent,
1366+ rgb. greenComponent,
1367+ rgb. blueComponent,
1368+ rgb. alphaComponent
1369+ )
1370+ }
1371+
12721372 @MainActor
12731373 private static func installCleanDesktopBackdropIfNeeded( arguments: [ String ] ) {
12741374 guard arguments. contains ( cleanDesktopFlag) else {
0 commit comments