+ PRE_WRAP . multipart_text_as_html($mime, $full_pfx) . '</pre>';
+}
+
+# this is already inside a <pre>
+# state = [ time, seen = {}, first_commit, page_nr = 0 ]
+sub index_entry {
+ my (undef, $mime, $level, $state) = @_;
+ my (undef, $seen, $first_commit) = @$state;
+ my $midx = $state->[3]++;
+ my ($prev, $next) = ($midx - 1, $midx + 1);
+ my $rv = '';
+ my $part_nr = 0;
+ my $enc_msg = enc_for($mime->header("Content-Type"));
+ my $subj = $mime->header('Subject');
+ my $header_obj = $mime->header_obj;
+
+ my $mid_raw = $header_obj->header_raw('Message-ID');
+ my $id = anchor_for($mid_raw);
+ $seen->{$id} = "#$id"; # save the anchor for later
+
+ my $mid = PublicInbox::Hval->new_msgid($mid_raw);
+ my $from = PublicInbox::Hval->new_oneline($mime->header('From'))->raw;
+ my @from = Email::Address->parse($from);
+ $from = $from[0]->name;
+ (defined($from) && length($from)) or $from = $from[0]->address;
+
+ $from = PublicInbox::Hval->new_oneline($from)->as_html;
+ $subj = PublicInbox::Hval->new_oneline($subj)->as_html;
+ my $pfx = (' ' x $level);
+ my $root_anchor = $seen->{root_anchor};
+ my $path;
+ my $more = 'permalink';
+ if ($root_anchor) {
+ $path = '../';
+ $subj = "<u\nid=\"u\">$subj</u>" if $root_anchor eq $id;
+ } else {
+ $path = '';
+ }
+
+ my $ts = $mime->header('X-PI-TS');
+ my $fmt = '%Y-%m-%d %H:%M UTC';
+ $ts = POSIX::strftime($fmt, gmtime($ts));
+
+ $rv .= "$pfx<b\nid=\"$id\">$subj</b>\n$pfx";
+ $rv .= "- by $from @ $ts - ";
+ $rv .= "<a\nid=\"s$midx\"\nhref=\"#s$next\">next</a>";
+ if ($prev >= 0) {
+ $rv .= "/<a\nhref=\"#s$prev\">prev</a>";
+ }
+ $rv .= "\n\n";
+
+ my $irt = $header_obj->header_raw('In-Reply-To');
+ my ($anchor_idx, $anchor, $t_anchor);
+ if (defined $irt) {
+ $anchor_idx = anchor_for($irt);
+ $anchor = $seen->{$anchor_idx};
+ $t_anchor = T_ANCHOR;
+ } else {
+ $t_anchor = '';
+ }
+ my $href = $mid->as_href;
+ my $mhref = "${path}m/$href.html";
+ my $fhref = "${path}f/$href.html";
+ # scan through all parts, looking for displayable text
+ $mime->walk_parts(sub {
+ $rv .= index_walk($_[0], $pfx, $enc_msg, $part_nr, $fhref,
+ \$more);
+ $part_nr++;
+ });
+
+ $rv .= "\n$pfx<a\nhref=\"$mhref\">$more</a> ";
+ my $txt = "${path}m/$href.txt";
+ $rv .= "<a\nhref=\"$txt\">raw</a> ";
+ $rv .= html_footer($mime, 0);
+
+ if (defined $irt) {
+ unless (defined $anchor) {
+ my $v = PublicInbox::Hval->new_msgid($irt);
+ $v = $v->as_href;
+ $anchor = "${path}m/$v.html";
+ $seen->{$anchor_idx} = $anchor;
+ }
+ $rv .= " <a\nhref=\"$anchor\">parent</a>";
+ }
+
+ if ($first_commit) {
+ $rv .= " <a\nhref=\"t/$href.html$t_anchor\">thread</a>";
+ }
+
+ $rv . "\n\n";
+}
+
+sub thread_html {
+ my (undef, $ctx, $foot, $srch) = @_;
+ my $mid = mid_compressed($ctx->{mid});
+ my $res = $srch->get_thread($mid);
+ my $rv = '';
+ my $msgs = load_results($ctx, $res);
+ my $nr = scalar @$msgs;
+ return $rv if $nr == 0;
+ require PublicInbox::Thread;
+ my $th = PublicInbox::Thread->new(@$msgs);
+ $th->thread;
+ $th->order(*PublicInbox::Thread::sort_ts);
+ my $state = [ undef, { root_anchor => anchor_for($mid) }, undef, 0 ];
+ thread_entry(\$rv, $state, $_, 0) for $th->rootset;
+ my $final_anchor = $state->[3];
+ my $next = "<a\nid=\"s$final_anchor\">end of thread</a>\n";
+
+ $rv .= "</pre><hr />" . PRE_WRAP . $next . $foot . "</pre>";