Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,7 @@ public struct HomeLiveRun: Hashable, Sendable {
/// The run's talk steps in order, joined by a blank line ("" before the first text).
public var text: String
public var activity: HomeLiveActivity?
/// The headline of the run's newest thought (``HomeThoughtHeadline``), kept after its step ends
/// until the next thought; nil when there is none.
/// The newest thought the run streams now (a thought step not yet ended), whole; nil when none.
/// Home's running indicator shows its headline as the Chief's progress narration.
public var thought: String? = nil

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ struct HomeLiveBook {
.sorted { (Self.place(run.steps[$0.element]), $0.offset) < (Self.place(run.steps[$1.element]), $1.offset) }
.compactMap { run.steps[$0.element]?.text }
.filter { !$0.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty }
let thought = run.lastThought.flatMap(HomeThoughtHeadline.of)
let thought = run.lastThought.flatMap { $0.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ? nil : $0 }
return HomeLiveRun(participant: key.author, run: key.run, turn: turn(of: key),
text: talk.joined(separator: "\n\n"), activity: run.activity, thought: thought)
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ struct HomeTurnProgress: Equatable {
/// Lines the owner sends for its own writing, not a step of the turn's work.
static let notSteps: Set<String> = ["Thinking", "Writing the reply", "Working"]
/// Most characters a narration row keeps.
static var narrationLimit: Int { HomeThoughtHeadline.limit }
static let narrationLimit = 72

init(summary: String?, narration: String?, current: String) {
self.summary = summary
Expand All @@ -54,8 +54,7 @@ struct HomeTurnProgress: Equatable {
}
let finished = steps.filter { $0.end != nil }.map(\.line).filter { !Self.notSteps.contains($0) }
let summary = Self.summary(finished)
// `thought` is already the headline (the live book publishes `HomeThoughtHeadline.of`).
var narration = style == .narrated ? thought : nil
var narration = style == .narrated ? thought.flatMap(Self.headline) : nil
var current = line
if let told = narration, activity.line == "Thinking" {
// Thinking now: its headline is the running row, not a second row over "Thinking".
Expand Down Expand Up @@ -127,6 +126,38 @@ struct HomeTurnProgress: Equatable {

// MARK: Narration

/// A thought's headline (``HomeThoughtHeadline``): the store already publishes it this way.
static func headline(_ thought: String) -> String? { HomeThoughtHeadline.of(thought) }
/// A thought's headline: its last bold heading line ("**Checking the tests**", how Claude and
/// Codex title their reasoning summaries), else its last sentence, as plain text of at most
/// ``narrationLimit`` characters. Nil for a thought with no words.
static func headline(_ thought: String) -> String? {
let lines = thought.split(whereSeparator: \.isNewline).map { $0.trimmingCharacters(in: .whitespaces) }
.filter { !$0.isEmpty }
let heading = lines.last { $0.count > 4 && $0.hasPrefix("**") && $0.hasSuffix("**") }
let raw: String
if let heading {
raw = String(heading.dropFirst(2).dropLast(2))
} else if let last = lines.last {
raw = lastSentence(last)
} else {
return nil
}
let plain = raw.replacingOccurrences(of: "**", with: "").replacingOccurrences(of: "`", with: "")
.trimmingCharacters(in: CharacterSet(charactersIn: "*_#> ").union(.whitespaces))
guard !plain.isEmpty else { return nil }
guard plain.count > narrationLimit else { return plain }
return String(plain.prefix(narrationLimit - 1)).trimmingCharacters(in: .whitespaces) + "…"
}

/// The last sentence of `line` (after its last ". ", "! " or "? "), or the line.
static func lastSentence(_ line: String) -> String {
let trimmed = line.trimmingCharacters(in: .whitespaces)
let body = trimmed.last.map { ".!?".contains($0) } == true ? String(trimmed.dropLast()) : trimmed
// After the last ". ", then the last "! " after it, then the last "? ": after the last end of any kind.
var rest = body
for end in [". ", "! ", "? "] {
if let range = rest.range(of: end, options: .backwards) { rest = String(rest.suffix(from: range.upperBound)) }
}
let sentence = rest.trimmingCharacters(in: .whitespaces)
return sentence.isEmpty ? trimmed : sentence
}
}
Loading