`
func formatContentForExport(text string) string {
s := html.EscapeString(text)
s = codeBlockRe.ReplaceAllString(s, "
$2
")
s = inlineCodeRe.ReplaceAllString(s, "$1")
s = thinkingMarkedRe.ReplaceAllString(s, thinkingHTML)
s = thinkingLegacyRe.ReplaceAllString(s, thinkingHTML)
s = toolBlockRe.ReplaceAllString(s,
`
[$1$2]$3
`)
return s
}
func isThinkingOnly(content string) bool {
if content == "" {
return false
}
without := thinkingMarkedRe.ReplaceAllString(content, "")
without = thinkingLegacyRe.ReplaceAllString(without, "")
return strings.TrimSpace(without) == ""
}
func focusedExportOrdinals(msgs []db.Message) map[int]bool {
visible := make(map[int]bool, len(msgs))
pendingOrdinal := 0
hasPendingAssistant := false
toolAfterPendingAssistant := false
flushPending := func() {
if hasPendingAssistant && !toolAfterPendingAssistant {
visible[pendingOrdinal] = true
}
hasPendingAssistant = false
toolAfterPendingAssistant = false
}
for _, m := range msgs {
if m.IsCompactBoundary {
flushPending()
visible[m.Ordinal] = true
continue
}
if m.IsSystem || db.IsGoalContextPrefixed(m.Content, m.Role) ||
isThinkingOnly(m.Content) {
continue
}
if isExportToolOnly(m) {
if hasPendingAssistant {
toolAfterPendingAssistant = true
}
continue
}
if m.Role == "user" {
flushPending()
visible[m.Ordinal] = true
continue
}
// Match the app's focused transcript mode: consecutive
// assistant-like messages collapse to the last visible answer.
pendingOrdinal = m.Ordinal
hasPendingAssistant = true
toolAfterPendingAssistant = false
}
flushPending()
return visible
}
func isExportToolOnly(m db.Message) bool {
if m.Role != "assistant" || !m.HasToolUse {
return false
}
for _, segment := range parseMarkdownSegments(m) {
switch segment.Type {
case markdownSegmentThinking, markdownSegmentTool:
continue
case markdownSegmentText:
if strings.TrimSpace(segment.Content) == "" {
continue
}
return false
default:
return false
}
}
return true
}
// parseTimestamp tries RFC3339Nano then RFC3339.
func parseTimestamp(ts string) (time.Time, bool) {
t, err := time.Parse(time.RFC3339Nano, ts)
if err != nil {
t, err = time.Parse(time.RFC3339, ts)
}
return t, err == nil
}
func formatTimestamp(ts string) string {
if ts == "" {
return ""
}
t, ok := parseTimestamp(ts)
if !ok {
return ts
}
return t.Format("2006-01-02 15:04:05")
}
func formatDateShort(ts *string) string {
if ts == nil || *ts == "" {
return "unknown"
}
t, ok := parseTimestamp(*ts)
if !ok {
return "unknown"
}
return t.Format("20060102")
}
func sanitizeFilename(name string) string {
re := regexp.MustCompile(`[^\w.\-]`)
return re.ReplaceAllString(name, "_")
}
func insightExportStem(insight *db.Insight) string {
dateRange := strings.ReplaceAll(insight.DateFrom, "-", "")
if insight.DateTo != "" && insight.DateTo != insight.DateFrom {
dateRange += "-" + strings.ReplaceAll(insight.DateTo, "-", "")
}
return sanitizeFilename(fmt.Sprintf(
"insight-%s-%s-%s",
insight.Type,
insightProjectLabel(insight.Project),
dateRange,
))
}
func insightExportHTMLFilename(insight *db.Insight) string {
return insightExportStem(insight) + ".html"
}
func insightExportMarkdownFilename(insight *db.Insight) string {
return insightExportStem(insight) + ".md"
}
func insightExportTitle(insight *db.Insight) string {
return fmt.Sprintf(
"%s Insight",
insightTypeLabel(insight.Type),
)
}
func insightPublishDescription(insight *db.Insight) string {
return fmt.Sprintf(
"Insight: %s - %s - %s",
insightTypeLabel(insight.Type),
insightProjectLabel(insight.Project),
insightDateRangeLabel(insight.DateFrom, insight.DateTo),
)
}
func insightTypeLabel(insightType string) string {
switch insightType {
case "daily_activity":
return "Daily Activity"
case "agent_analysis":
return "Agent Analysis"
default:
return strings.ReplaceAll(insightType, "_", " ")
}
}
func insightProjectLabel(project *string) string {
value := strings.TrimSpace(derefString(project))
if value == "" {
return "global"
}
return value
}
func insightDateRangeLabel(dateFrom, dateTo string) string {
if dateTo == "" || dateTo == dateFrom {
return dateFrom
}
return dateFrom + " to " + dateTo
}
func publishExportHTML(
ctx context.Context,
token, filename, description, htmlContent string,
public bool,
) (*publishResponse, error) {
gist, err := createGist(ctx, token, filename, description, htmlContent, public)
if err != nil {
return nil, err
}
if gist.ID == "" || gist.HTMLURL == "" {
return nil, fmt.Errorf("GitHub API returned incomplete gist data")
}
encoded := urlPathEscape(filename)
rawURL := fmt.Sprintf(
"https://gist.githubusercontent.com/%s/%s/raw/%s",
gist.Owner.Login, gist.ID, encoded,
)
return &publishResponse{
GistID: gist.ID,
GistURL: gist.HTMLURL,
ViewURL: "https://htmlpreview.github.io/?" + rawURL,
RawURL: rawURL,
}, nil
}
func derefString(value *string) string {
if value == nil {
return ""
}
return *value
}
func truncateStr(s string, max int) string {
if len(s) <= max {
return s
}
// Truncate at a valid rune boundary to avoid producing
// invalid UTF-8.
r := []rune(s)
if len(r) <= max {
return s
}
return string(r[:max]) + "..."
}