• OSC 8 detection / support

    From Rob Swindell@1:103/705 to GitLab note in main/sbbs on Fri Nov 6 16:11:14 2026
    https://gitlab.synchro.net/main/sbbs/-/work_items/872#note_10809

    Use case from 2026-11-06: a user reading a GitLab "Reset password instructions" email in a BBS terminal session could not reset their password. The reset link is 88 characters (`https://gitlab.synchro.net/users/password/edit?reset_password_token=` is 68, plus a 20-character token). The message display word-wraps at `cols - 1` = 79, so the last 9 characters of the token land on the next line. Every click opened a 79-character URL with an 11-character token, and GitLab reports any token it does not recognize as "expired", which the user read as the tokens timing out. The GitLab account-confirmation link has the same problem at 85 characters. Any URL longer than 79 characters in any message is affected.

    This is a case the design above does not cover: nobody wrote a script or menu file. The URL is in message text that `show_msg`/`putmsg` displays with `P_WORDWRAP`. To help here, the terminal server needs to link URLs it finds in message text automatically, not only where a script or @-code asks for it.

    Notes for that automatic case:

    - The visible text should stay the full URL, wrapped exactly as today. OSC 8 only adds the out-of-band URI around it, so terminals without OSC 8 see no change, and copy/paste still works. The short-label form ("[click here]") from the design above belongs to the explicit `hyperlink(uri, text)` case only.
    - Because the hyperlink is a state applied to subsequent cells, one OSC 8 open before the URL and one close after it covers both wrapped fragments; the CRLF between them does not end the region (`src/conio/cterm.adoc`, "OSC 8"). No `id=` parameter is needed.
    - `src/sbbs3/wordwrap.c` skips Ctrl-A codes and `ESC [` CSI sequences as zero-width but has no handling for OSC (`ESC ]` ... `ESC \`). An OSC 8 sequence inserted before wrapping would count toward the column and could be split. Either teach `wordwrap.c` to skip OSC strings, or apply the linking to the wrapped output. The output-side column tracking in the Terminal class already handles DCS/OSC/PM/APC control strings.
    - SyncTERM's own click-to-open URL detection (`detect_url_at()` in `src/syncterm/term.c`) joins a URL across rows only when the previous row's last cell (column 79 of 80) holds a URL character. Synchronet's wrap leaves column 79 blank, so that heuristic does not recover these links either; OSC 8 is the fix on the Synchronet side.
    - This would not have helped the user in question, who was in the fTelnet web terminal, unless fTelnet implements OSC 8 (not checked). For GitLab's own links, a short redirect path in GitLab's nginx (e.g. `/pw/<token>`, 50 characters) plus a rewrite of the link in mail delivered to BBS mailboxes reaches every terminal; that is independent of this issue.

    *Authored by Claude (Claude Code), on behalf of @rswindell*
    --- SBBSecho 3.38-Linux
    * Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705)