0043d5ee22db1de1ec305eadb8442bd5a59fe5d7
max
  Mon Aug 10 08:55:00 2026 -0700
hgc: route non-BLAT alignment clicks to the modern single-page view, refs #37893

Behind a new modernAlignPage hg.conf gate (default off), ordinary alignment
details -- mRNA/EST (htcCdnaAli), PSL/bigPsl, protein, cross-species and the
other showSomeAlignment callers -- now render in the modern single-page
alignment instead of the classic two-frame <frameset>, matching hgBlat's new
results page.

- Generalize showSomeAlignmentModern with a blatContext flag: TRUE keeps the
hgBlat chrome (title 'BLAT Base Alignment', 'Back to results', 'Share a
link'); FALSE is a plain track click, which has no BLAT results to return to
or share, so the title is just 'Base Alignment' and those buttons are omitted.
- alnModernStart() starts modern chrome and arms a flag when modernAlignPage is
set; showSomeAlignment() then renders the modern body, else the classic
frameset. htmlFramesetStart() is left untouched (it is shared with transMap,
retro and pubs click pages), so only the converted callers change.
- The window-restricted (showSomePartialDnaAlignment) and Lowe-Lab
(showSomeAlignment2) renderers are left classic for now.
- Register modernAlignPage in hgConfCatalog.

diff --git src/hg/hgc/hgc.c src/hg/hgc/hgc.c
index 1e224043630..504f1d89523 100644
--- src/hg/hgc/hgc.c
+++ src/hg/hgc/hgc.c
@@ -8354,35 +8354,51 @@
 fprintf(body, "<H2>Alignment of %s and %s:%d-%d</H2>\n",
 	psl->qName, displayChromName, partTStart+1, partTEnd);
 if (!cartUsualBoolean(cart, "blatNewPage", FALSE))  /* no "frame" in the new single-page view */
     fprintf(body, "Click on links in the frame to the left to navigate through "
 	"the alignment.\n");
 
 blockCount = ffShAliPart(body, ffAli, wholePsl->qName,
                          rna + rnaStart, rnaEnd - rnaStart, rnaStart,
 			 displayChromName, dnaSeq->dna, dnaSeq->size,
 			 wholeTStart, 8, FALSE, isRc,
 			 FALSE, TRUE, TRUE, TRUE, TRUE,
                          cdsS, cdsE, partTStart, partTEnd);
 return blockCount;
 }
 
+/* The modern single-page alignment (webStartGbNoBanner chrome, no <frameset>) can now stand in for
+ * the classic frameset on any alignment page, not just hgBlat's - gated by the modernAlignPage
+ * hg.conf flag.  alnModernStart() starts the right page chrome and sets gAlnModern; showSomeAlignment()
+ * then renders the modern or classic body to match.  These are forward-declared here because
+ * showSomeAlignment() sits above their definitions. */
+static boolean gAlnModern = FALSE;
+static void showSomeAlignmentModern(struct psl *psl, bioSeq *oSeq, enum gfType qType,
+                       int qStart, int qEnd, char *qName, int cdsS, int cdsE, boolean blatContext);
+static void alnModernStart(char *classicTitle);
+
 void showSomeAlignment(struct psl *psl, bioSeq *oSeq,
                        enum gfType qType, int qStart, int qEnd,
                        char *qName, int cdsS, int cdsE)
-/* Display protein or DNA alignment in a frame. */
+/* Display protein or DNA alignment in a frame (or the modern single page when gAlnModern). */
 {
+if (gAlnModern)
+    {   /* modern single-page view; blatContext=FALSE -> neutral chrome (no BLAT title/buttons) */
+    showSomeAlignmentModern(psl, oSeq, qType, qStart, qEnd, qName, cdsS, cdsE, FALSE);
+    webEndGb();
+    exit(0);   // we drew the whole page; skip the frameset close
+    }
 int blockCount, i;
 struct tempName indexTn, bodyTn;
 FILE *index, *body;
 
 trashDirFile(&indexTn, "index", "index", ".html");
 trashDirFile(&bodyTn, "body", "body", ".html");
 
 /* Writing body of alignment. */
 body = mustOpen(bodyTn.forCgi, "w");
 htmStartDirDepth(body, psl->qName, 2);
 if (qType == gftRna || qType == gftDna)
     blockCount = showPartialDnaAlignment(psl, oSeq, body, cdsS, cdsE, FALSE);
 else
     blockCount = showGfAlignment(psl, oSeq, body, qType, qStart, qEnd, qName);
 htmEnd(body);
@@ -8524,31 +8540,31 @@
 if (isCustomTrack(aliTable))
     {
     struct customTrack *ct = lookupCt(aliTable);
     tdb = ct->tdb;
     }
 else
     tdb = hashFindVal(trackHash, aliTable);
 if (tdb == NULL)
     errAbort("BUG: bigPsl alignment table '%s' not found; this maybe causes by `.' in track names", aliTable);
              
 if (!trackHubDatabase(database))
     conn = hAllocConnTrack(database, tdb);
 
 char title[1024];
 safef(title, sizeof title, "%s vs Genomic [%s]", acc, aliTable);
-htmlFramesetStart(title);
+alnModernStart(title);
 
 /* Get some environment vars. */
 start = cartInt(cart, "l");
 int end = cartInt(cart, "r");
 char *chrom = cartString(cart, "c");
 
 char *seq, *cdsString = NULL;
 struct lm *lm = lmInit(0);
 char *fileName = bbiNameFromSettingOrTable(tdb, conn, tdb->table);
 struct bbiFile *bbi =  bigBedFileOpenAlias(fileName, chromAliasFindAliases);
 struct bigBedInterval *bb, *bbList = bigBedIntervalQuery(bbi, chrom, start, end, 0, lm);
 char *bedRow[32];
 char startBuf[16], endBuf[16];
 for (bb = bbList; bb != NULL; bb = bb->next)
     {
@@ -8678,31 +8694,31 @@
 char **row;
 struct psl *psl;
 struct dnaSeq *rnaSeq;
 char *aliTable;
 int start;
 unsigned int cdsStart = 0, cdsEnd = 0;
 boolean hasBin;
 char accChopped[512] ;
 safef(accChopped, sizeof(accChopped), "%s",acc);
 chopSuffix(accChopped);
 
 aliTable = cartString(cart, "aliTable");
 char *accForTitle = startsWith("ncbiRefSeq", aliTable) ? acc : accChopped;
 char title[1024];
 safef(title, sizeof title, "%s vs Genomic [%s]", accForTitle, aliTable);
-htmlFramesetStart(title);
+alnModernStart(title);
 
 /* Get some environment vars. */
 start = cartInt(cart, "o");
 
 conn = hAllocConn(database);
 getCdsStartAndStop(conn, acc, aliTable, &cdsStart, &cdsEnd);
 
 /* Look up alignments in database */
 if (!hFindSplitTable(database, seqName, aliTable, table, sizeof table, &hasBin))
     errAbort("Failed to find aliTable=%s", aliTable);
 sqlSafef(query, sizeof query, "select * from %s where qName like '%s%%' and tName=\"%s\" and tStart=%d",
 	table, acc, seqName, start);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) == NULL)
     errAbort("Couldn't find alignment for %s at %d", acc, start);
@@ -8942,31 +8958,31 @@
     qSeq = loadGenomePart(otherDb, psl->qName, psl->qStart, psl->qEnd);
     safef(name, sizeof name, "%s.%s", otherOrg, psl->qName);
     }
 else if (otherTbf != NULL)
     {
     qSeq = twoBitReadSeqFragLower(otherTbf, psl->qName, psl->qStart, psl->qEnd);
     safef(name, sizeof name, "%s", psl->qName);
     }
 if (qSeq == NULL)
     {
     errAbort("Can't find query sequence in htcChainAli");
     }
 char title[1024];
 safef(title, sizeof title, "%s %s vs %s %s ",
        (otherOrg == NULL ? "" : otherOrg), psl->qName, org, psl->tName );
-htmlFramesetStart(title);
+alnModernStart(title);
 showSomeAlignment(psl, qSeq, gftDnaX, psl->qStart, psl->qEnd, name, 0, 0);
 }
 
 void htcChainTransAli(char *item)
 /* Draw detailed alignment representation of a chain with translated protein */
 {
 struct chain *chain;
 struct psl *fatPsl, *psl = NULL;
 char *track = cartString(cart, "o");
 char *type = trackTypeInfo(track);
 char *typeWords[2];
 char *otherDb = NULL, *org = NULL, *otherOrg = NULL;
 struct dnaSeq *qSeq = NULL;
 char name[128];
 int cdsStart = cgiInt("qs");
@@ -8999,94 +9015,97 @@
 pslFree(&fatPsl);
 
 if (sameWord(otherDb, "seq"))
     {
     qSeq = hExtSeq(database, psl->qName);
     safef(name, sizeof name, "%s", psl->qName);
     }
 else
     {
     qSeq = loadGenomePart(otherDb, psl->qName, psl->qStart, psl->qEnd);
     safef(name, sizeof name, "%s.%s", otherOrg, psl->qName);
     }
 char title[1024];
 safef(title, sizeof title, "%s %s vs %s %s ",
        (otherOrg == NULL ? "" : otherOrg), psl->qName, org, psl->tName );
-htmlFramesetStart(title);
+alnModernStart(title);
 /*showSomeAlignment(psl, qSeq, gftDnaX, psl->qStart, psl->qEnd, name, 0, 0); */
 showSomeAlignment(psl, qSeq, gftDnaX, psl->qStart, psl->qEnd, name, cdsStart, cdsEnd);
 }
 
 static char *blatAsmLabel(char *database)
 /* A user-facing assembly label for the page title.  For an assembly hub the internal
  * "hub_NNN_GCA_..." database name is not helpful, so use the assembly's friendly organism plus its
  * accession; for a native assembly just use the db name (e.g. "hg38"). */
 {
 if (!trackHubDatabase(database))
     return cloneString(database);
 char *acc = trackHubSkipHubName(database);   /* drop the "hub_NNN_" prefix -> the accession */
 char *org = hGenome(acc);                    /* GenArk table's friendly genome name for GC* accs */
 if (isEmpty(org))
     {
     org = trackHubAssemblyField(database, "organism");   /* else the hub's genomes.txt organism */
     org = trackHubSkipHubName(org);          /* strip the "hub_NNN_" prefix addHubName() baked in */
     }
 if (isEmpty(org))
     return cloneString(acc);
 char buf[256];
 safef(buf, sizeof buf, "%s %s", org, acc);
 return cloneString(buf);
 }
 
 static void showSomeAlignmentModern(struct psl *psl, bioSeq *oSeq, enum gfType qType,
-                       int qStart, int qEnd, char *qName, int cdsS, int cdsE)
-/* Modern single-page version of showSomeAlignment for hgBlat's new table mode: a gold title bar with
- * Back/Share buttons, a full-height "jump to" sidebar, then an "Alignment Summary" and the base-by-
- * base alignment inlined below with steel-blue section headers, so the whole page scrolls (no
- * <frameset>).  The alignment body itself is generated by the shared library as before.  The caller
- * supplies the page chrome via webStartGbNoBanner()/webEndGb() - a menubar and <main> with no legacy
- * section tables - so everything here is plain, table-free HTML. */
+                       int qStart, int qEnd, char *qName, int cdsS, int cdsE, boolean blatContext)
+/* Modern single-page version of showSomeAlignment: a gold title bar, a full-height "jump to"
+ * sidebar, then an "Alignment summary" and the base-by-base alignment inlined below with steel-blue
+ * section headers, so the whole page scrolls (no <frameset>).  The alignment body itself is
+ * generated by the shared library as before.  The caller supplies the page chrome via
+ * webStartGbNoBanner()/webEndGb() - a menubar and <main> with no legacy section tables - so
+ * everything here is plain, table-free HTML.  blatContext=TRUE is the hgBlat path: the title says
+ * "BLAT" and the bar carries "Back to results" and "Share a link"; blatContext=FALSE is a plain
+ * track click (mRNA/EST/PSL...), which has no BLAT results to go back to or share. */
 {
 if (qName == NULL)
     qName = psl->qName;
 char *chrom = chromAliasGetDisplayChrom(database, cart, psl->tName);
 /* Alternate (chromAlias) names for the genomic sequence - e.g. its RefSeq/GenBank/Ensembl accessions
  * - shown after the main name in the "Only genome sequence" header. */
 struct dyString *aliasDy = dyStringNew(128);
 struct slName *aliasList = chromAliasFindAliases(psl->tName), *al;
 struct hash *seenAlias = hashNew(0);
 hashStore(seenAlias, chrom);        /* skip the name already shown, and the native name */
 hashStore(seenAlias, psl->tName);
 boolean firstAlias = TRUE;
 for (al = aliasList; al != NULL; al = al->next)
     {
     if (isEmpty(al->name) || hashLookup(seenAlias, al->name))
         continue;   /* skip empties, the shown name, and duplicates (e.g. Ensembl and GenBank "7") */
     hashStore(seenAlias, al->name);
     dyStringPrintf(aliasDy, "%s%s", firstAlias ? "" : ", ", al->name);
     firstAlias = FALSE;
     }
 hashFree(&seenAlias);
 char *aliasStr = dyStringCannibalize(&aliasDy);   /* "NC_000007.14, CM000669.2, 7" or "" */
 double ident = 100.0 - pslCalcMilliBad(psl, TRUE) * 0.1;
 
 char *idColor = (ident >= 98) ? "#1f7a34" : (ident >= 95) ? "#4d7c0f" :
                 (ident >= 90) ? "#b45309" : "#b1301f";
 
-/* Offer "Share a link" only when a durable bigPsl custom track backs these results; without it there
- * is nothing for a shared session to rebuild the alignment from. */
-char *shareBb = blatFindPinnedBigPsl(cart);
+/* Offer "Share a link" only in the hgBlat context, and only when a durable bigPsl custom track
+ * backs these results; without it there is nothing for a shared session to rebuild the alignment
+ * from.  A plain track click has no BLAT session to share. */
+char *shareBb = blatContext ? blatFindPinnedBigPsl(cart) : NULL;
 boolean canShare = (shareBb != NULL);
 freeMem(shareBb);
 
 /* Colors imported from the BLAT Redesign (slide 3): grey page, steel-blue section-header bars, navy
  * links with maroon hover, slate text.  The <h2>/<hr> the shared alignment code emits are hidden; its
  * <h4> section headings become the steel-blue bars.  The page chrome is webStartGbNoBanner (a menubar
  * and <main>, no legacy section tables), so we draw our own gold title bar in plain HTML. */
 printf("<style>"
        "#main-menu-whole{margin-bottom:0}"   /* no gap between the menubar and the title bar */
        "#mainContent{background:#eef1f4}"     /* grey page behind the white alignment panel */
        ".blatTitleBar{background:#eaca92; color:#000; box-sizing:border-box; display:flex;"
        " align-items:center; justify-content:space-between; padding:8px 16px}"  /* gold title band */
        ".blatTitleBar .blatTtl{font-size:18px; font-weight:700}"
        ".blatTitleBar .blatBtns{display:flex; gap:8px; align-items:center}"
        ".blatBtn{padding:4px 12px; font-size:13px; border:1px solid #999; border-radius:3px;"
@@ -9113,35 +9132,38 @@
        " font-size:15px; font-weight:700}"                     /* -20px: bar spans full content width */
        "#blatAlnContent h4:first-child{margin-top:0}"          /* Alignment Summary flush at top */
        "#blatAlnContent h4 a{color:#fff}"
        /* undo bootstrap.css (pulled in by webStartGbNoBanner's gbHeader) on the sequence blocks:
         * it would give <pre> a grey box, a border and word-break that mangles the alignment */
        "#blatAlnContent pre{margin:0; padding:2px 0 12px; line-height:1.4; background:none; border:0;"
        " border-radius:0; color:#374a5e; white-space:pre; word-break:normal; word-wrap:normal}"
        /* key-value summary strip, mirroring hgBlat's .blatStrip (label over value, thin dividers) */
        ".blatAlnStrip{display:flex; align-items:center; gap:24px; flex-wrap:wrap; margin:2px 0 14px}"
        ".blatAlnStat{display:flex; flex-direction:column; gap:1px}"
        ".blatAlnStat .k{font-size:12px; color:#5b6572; font-weight:700}"
        ".blatAlnStat .v{font-size:14px; color:#1e2833; font-weight:700}"
        ".blatAlnStrip .d{width:1px; height:28px; background:#d0d0d0}"
        "</style>\n");
 
-/* gold title bar, drawn directly (no framework subheadingBar, no JS): title on the left, then a
- * "Share a link" button (when a durable track backs the results) and a "Back to results" button. */
+/* gold title bar, drawn directly (no framework subheadingBar, no JS): title on the left, then (in
+ * the hgBlat context only) a "Back to results" and, when a durable track backs the results, a
+ * "Share a link" button. */
 printf("<div class='blatTitleBar'>");
-printf("<span class='blatTtl'>BLAT Base Alignment: %s</span>", blatAsmLabel(database));
+printf("<span class='blatTtl'>%s Base Alignment: %s</span>",
+       blatContext ? "BLAT" : "", blatAsmLabel(database));
 printf("<span class='blatBtns'>");
+if (blatContext)
     printf("<a href='hgBlat?blatReopen=1&hgsid=%s' class='blatBtn'>"
            "\xe2\x80\xb9 Back to results</a>", cartSessionId(cart));
 if (canShare)
     printf("<a href='#' id='blatShareBtn' class='blatBtn'>Share a link</a>");
 printf("</span></div>\n");
 
 /* one white panel laid out as two grid columns: a full-height "jump to" sidebar on the left, and on
  * the right an "Alignment Summary" header, the summary line, and the base-by-base alignment inlined
  * so the whole page scrolls */
 printf("<div id='blatAlnBody'>\n");
 
 printf("<div id='blatAlnContent'>\n");
 printf("<h4>Alignment summary</h4>\n");
 /* comma-format the coordinates and base counts, matching the new Table view (readable at the
  * hundreds-of-millions scale of genomic coordinates, and the convention elsewhere in the browser) */
@@ -9256,43 +9278,61 @@
     " headers:{'Content-Type':'application/x-www-form-urlencoded'},"
     " body:'hgsid=%s&hgS_doSaveSessionJson=1&hgS_shareAnon=1'})\n"
     "  .then(function(r){ return r.json(); }).then(function(data){\n"
     "    btn.textContent = label; btn.dataset.busy = '';\n"
     "    if (!data || !data.name) return;\n"
     "    var link = window.location.origin + window.location.pathname +\n"
     "      '?g=htcBlatAlign&c=%s&o=%d&i=' + encodeURIComponent('%s') +\n"  // db comes from the session
     "      '&u=l&s=' + encodeURIComponent(data.name);\n"
     "    if (window.topLinks && topLinks.shareUrl) topLinks.shareUrl(link);\n"
     "  }).catch(function(){ btn.textContent = label; btn.dataset.busy = ''; });\n"
     "});\n"
     "})();\n",
     cartSessionId(cart), psl->tName, psl->tStart, qName);
 }
 
+static void alnModernStart(char *classicTitle)
+/* Begin an alignment page.  With the modernAlignPage hg.conf flag set, start the modern single-page
+ * chrome (webStartGbNoBanner) and arm gAlnModern so showSomeAlignment() renders the modern body;
+ * otherwise start the classic <frameset>.  This is the plain track-click entry point (mRNA/EST/PSL
+ * details), so the modern page is drawn in its neutral, non-BLAT form. */
+{
+if (cfgOptionBooleanDefault("modernAlignPage", FALSE))
+    {
+    gAlnModern = TRUE;
+    char pageTitle[256];
+    safef(pageTitle, sizeof pageTitle, "Base Alignment: %s", blatAsmLabel(database));
+    webStartGbNoBanner(cart, database, pageTitle);   // menubar + <main>, no legacy section tables
+    }
+else
+    htmlFramesetStart(classicTitle);
+}
+
 void htcUserAli(char *fileNames)
 /* Show alignment for accession. */
 {
 char *pslName, *faName, *qName;
 struct lineFile *lf;
 bioSeq *oSeqList = NULL, *oSeq = NULL;
 struct psl *psl;
 int start;
 enum gfType tt, qt;
 boolean isProt;
-/* In hgBlat's new table mode (blatNewPage) show a modern single-page alignment instead of the
- * classic two-frame <frameset>. */
-boolean modern = cartUsualBoolean(cart, "blatNewPage", FALSE);
+/* In hgBlat's new table mode (blatNewPage), or wherever the modernAlignPage flag is set, show a
+ * modern single-page alignment instead of the classic two-frame <frameset>. */
+boolean modern = cartUsualBoolean(cart, "blatNewPage", FALSE)
+                 || cfgOptionBooleanDefault("modernAlignPage", FALSE);
 
 char title[1024];
 safef(title, sizeof title, "User Sequence vs Genomic");
 if (modern)
     {
     char pageTitle[256];
     safef(pageTitle, sizeof pageTitle, "BLAT Base Alignment: %s", blatAsmLabel(database));
     webStartGbNoBanner(cart, database, pageTitle);   // menubar + <main>, no legacy section tables
     }
 else
     htmlFramesetStart(title);
 
 start = cartInt(cart, "o");
 parseSs(fileNames, &pslName, &faName, &qName);
 if (modern && (!fileExists(pslName) || !fileExists(faName)))
@@ -9310,31 +9350,31 @@
         break;
     pslFree(&psl);
     }
 lineFileClose(&lf);
 if (psl == NULL)
     errAbort("Couldn't find alignment at %s:%d", seqName, start);
 oSeqList = faReadAllSeq(faName, !isProt);
 for (oSeq = oSeqList; oSeq != NULL; oSeq = oSeq->next)
     {
     if (sameString(oSeq->name, qName))
 	break;
     }
 if (oSeq == NULL)  errAbort("%s is in %s but not in %s. Internal error.", qName, pslName, faName);
 if (modern)
     {
-    showSomeAlignmentModern(psl, oSeq, qt, 0, oSeq->size, NULL, 0, 0);
+    showSomeAlignmentModern(psl, oSeq, qt, 0, oSeq->size, NULL, 0, 0, TRUE);   // hgBlat context
     webEndGb();
     exit(0);   // we drew the whole page; skip the framework's table-closing cartHtmlEnd
     }
 else
     showSomeAlignment(psl, oSeq, qt, 0, oSeq->size, NULL, 0, 0);        // classic frameset; exits itself
 }
 
 void htcBlatAlign(char *qName)
 /* Durable base-by-base alignment for a shared BLAT link (g=htcBlatAlign): rebuild one alignment from
  * the saved session's durable bigPsl custom track (blatLastBigBed) instead of the ephemeral trash
  * .pslx/.fa the fresh-search htcUserAli path reads.  seqName and o identify the hit; the query
  * sequence comes from the bigPsl record itself, so no stored trash sequence is needed.  This backs
  * the "Share a link" button on the modern alignment page. */
 {
 char pageTitle[256];
@@ -9347,54 +9387,54 @@
            "has expired or been removed. Please run a new <a href=\"hgBlat\">BLAT search</a>.</p>\n");
     webEndGb();
     exit(0);
     }
 int start = cartInt(cart, "o");
 char *seq = NULL;
 struct psl *psl = pslFromBigPslFileMatch(bbFile, seqName, start, qName, &seq, NULL);
 if (psl == NULL || seq == NULL)
     {
     printf("<p>This alignment was not found in the shared BLAT results.</p>\n");
     webEndGb();
     exit(0);
     }
 enum gfType qType = pslIsProtein(psl) ? gftProt : gftDna;
 struct dnaSeq *oSeq = newDnaSeq(cloneString(seq), strlen(seq), qName);
-showSomeAlignmentModern(psl, oSeq, qType, 0, oSeq->size, NULL, 0, 0);
+showSomeAlignmentModern(psl, oSeq, qType, 0, oSeq->size, NULL, 0, 0, TRUE);   // hgBlat shared-link context
 webEndGb();
 exit(0);   // we drew the whole page; skip the framework's table-closing cartHtmlEnd
 }
 
 void htcProteinAli(char *readName, char *table)
 /* Show protein to translated dna alignment for accession. */
 {
 struct psl *psl;
 int start;
 enum gfType qt = gftProt;
 struct sqlResult *sr;
 struct sqlConnection *conn = hAllocConn(database);
 struct dnaSeq *seq = NULL;
 char query[256], **row;
 char fullTable[HDB_MAX_TABLE_STRING];
 boolean hasBin;
 char buffer[256];
 int addp = 0;
 char *pred = NULL;
 
 char title[1024];
 safef(title, sizeof title, "Protein Sequence vs Genomic");
-htmlFramesetStart(title);
+alnModernStart(title);
 
 addp = cartUsualInt(cart, "addp",0);
 pred = cartUsualString(cart, "pred",NULL);
 start = cartInt(cart, "o");
 if (!hFindSplitTable(database, seqName, table, fullTable, sizeof fullTable, &hasBin))
     errAbort("track %s not found", table);
 sqlSafef(query, sizeof query, "select * from %s where qName = '%s' and tName = '%s' and tStart=%d",
 	fullTable, readName, seqName, start);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) == NULL)
     errAbort("Couldn't find alignment for %s at %d", readName, start);
 psl = pslLoad(row+hasBin);
 sqlFreeResult(&sr);
 if ((addp == 1) || (pred != NULL))
     {
@@ -9435,31 +9475,31 @@
 
 void htcBlatXeno(char *readName, char *table)
 /* Show alignment for accession. */
 {
 struct psl *psl;
 int start;
 struct sqlResult *sr;
 struct sqlConnection *conn = hAllocConn(database);
 struct dnaSeq *seq;
 char query[256], **row;
 char fullTable[HDB_MAX_TABLE_STRING];
 boolean hasBin;
 
 char title[1024];
 safef(title, sizeof title, "Sequence %s", readName);
-htmlFramesetStart(title);
+alnModernStart(title);
 
 start = cartInt(cart, "o");
 if (!hFindSplitTable(database, seqName, table, fullTable, sizeof fullTable, &hasBin))
     errAbort("track %s not found", table);
 sqlSafef(query, sizeof query, "select * from %s where qName = '%s' and tName = '%s' and tStart=%d",
 	fullTable, readName, seqName, start);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) == NULL)
     errAbort("Couldn't find alignment for %s at %d", readName, start);
 psl = pslLoad(row+hasBin);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 seq = hExtSeq(database, readName);
 showSomeAlignment(psl, seq, gftDnaX, 0, seq->size, NULL, 0, 0);
 }
@@ -9785,31 +9825,31 @@
 {
 struct psl *psl;
 struct dnaSeq *seq;
 struct sqlResult *sr;
 struct sqlConnection *conn = hAllocConn(database);
 char query[256], **row;
 int start;
 char *pslTable = cgiUsualString("pslTable", "illuminaProbesAlign");
 char *seqTable = cgiUsualString("seqTable", "illuminaProbesSeq");
 char *probeName = item;
 char *probeString;
 int rowOffset = hOffsetPastBin(database, seqName, pslTable);
 
 char title[1024];
 safef(title, sizeof title, "Sequence %s", probeName);
-htmlFramesetStart(title);
+alnModernStart(title);
 start = cartInt(cart, "o");
 /* get psl */
 sqlSafef(query, sizeof(query), "select * from %s where qName = '%s' and tName = '%s' and tStart=%d",
 	pslTable, probeName, seqName, start);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) == NULL)
     errAbort("Couldn't find alignment for %s at %d", probeName, start);
 psl = pslLoad(row+rowOffset);
 sqlFreeResult(&sr);
 sqlSafef(query, sizeof(query), "select seq from %s where id = '%s'", seqTable, probeName);
 probeString = sqlNeedQuickString(conn, query);
 seq = newDnaSeq(probeString, strlen(probeString), probeName);
 hFreeConn(&conn);
 showSomeAlignment(psl, seq, gftDna, 0, seq->size, probeName, 0, 0);
 pslFree(&psl);
@@ -16300,31 +16340,31 @@
 qChrom = psl->qName;
 if ((ptr = strchr(qChrom, '.')) != NULL)
     qChrom = ptr+1;
 
 /* Make sure that otherOrg's chrom size matches psl's qSize */
 if (hChromSize(database, qChrom) != psl->qSize)
     errAbort("Alignment's query size for %s is %d, but the size of %s in database %s is %d.  Incorrect database in trackDb.type?",
 	     qChrom, psl->qSize, qChrom, otherDb, hChromSize(otherDb, qChrom));
 
 psl = pslTrimToTargetRange(psl, winStart, winEnd);
 
 qSeq = loadGenomePart(otherDb, qChrom, psl->qStart, psl->qEnd);
 snprintf(name, sizeof(name), "%s.%s", otherOrg, qChrom);
 char title[1024];
 safef(title, sizeof title, "%s %dk", name, psl->qStart/1000);
-htmlFramesetStart(title);
+alnModernStart(title);
 showSomeAlignment(psl, qSeq, gftDnaX, psl->qStart, psl->qEnd, name, 0, 0);
 }
 
 void doAlignCompGeno(struct trackDb *tdb, char *itemName, char *otherGenome)
     /* Handle click on blat or blastz track in a generic fashion */
     /* otherGenome is the text to display for genome name on details page */
 {
 char query[256];
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr = NULL;
 char **row;
 int start = cartInt(cart, "o");
 int end = cartInt(cart, "t");
 char *chrom = cartString(cart, "c");
 struct psl *pslList = NULL, *psl;