[wiki] [sites] [st][patches][scrollback-reflow-standalone] || elbachir-one

From: <git_AT_suckless.org>
Date: Fri, 16 Jan 2026 19:12:51 +0100

commit 6d93b8d8600844b14e8c8bcf507516ad480ff474
Author: elbachir-one <bachiralfa_AT_gmail.com>
Date: Fri Jan 16 19:05:48 2026 +0100

    [st][patches][scrollback-reflow-standalone]
    
        - Fixed index.md
        - Fixed patch names

diff --git a/st.suckless.org/patches/scrollback-reflow-standalone/index.md b/st.suckless.org/patches/scrollback-reflow-standalone/index.md
index 36b19e8e..1e80ba71 100644
--- a/st.suckless.org/patches/scrollback-reflow-standalone/index.md
+++ b/st.suckless.org/patches/scrollback-reflow-standalone/index.md
_AT_@ -19,10 +19,7 @@ Two variants are provided:
 * **Extended**: identical to the basic variant, but with persistent selection
   that remains valid even when scrolled outside the visible screen.
 
----
-
-Why another scrollback patch?
------------------------------
+#### Why another scrollback patch?
 
 One patch is easier to handle than many.
 
_AT_@ -40,10 +37,7 @@ behavior.
 The goal is not to replace existing solutions, but to provide a standalone
 alternative that emphasizes correctness and predictable behavior.
 
----
-
-Key Features
-------------
+#### Key Features
 
 * Ring buffer scrollback with O(1) insertion
 * Text reflow on resize (O(N) in scrollback size)
_AT_@ -55,10 +49,7 @@ Key Features
 * Stable selection while scrolling
 * Optional persistent selection (extended variant)
 
----
-
-Resize Reflow
--------------
+#### Resize Reflow
 
 When the terminal width changes, scrollback content is reflowed to match the new
 column width. Wrapped lines are flattened and rewrapped so that text is neither
_AT_@ -66,9 +57,7 @@ clipped nor lost when shrinking the window.
 
 To reduce overhead, reflow is skipped when it is not required.
 
----
-Which patch should I choose?
-------------
+#### Which patch should I choose?
 
 Choose the **extended** variant if you prefer selection behavior similar to
 most modern terminal emulators, where a selection remains valid even when
_AT_@ -79,10 +68,7 @@ Choose the **basic** variant if you prefer st’s traditional selection behavior
 where selection is limited to the visible screen, or if you plan to combine
 this patch with other modifications that interact with selection logic.
 
----
-
-Patch Compatibility
--------------------
+#### Patch Compatibility
 
 This patch modifies `tresize()` and related resize handling to support scrollback
 reflow and history preservation across window size changes.
_AT_@ -98,10 +84,7 @@ The extended variant relaxes certain selection bounds checks to allow selection
 to persist outside the visible screen. This may interact poorly with patches
 that make assumptions about screen-local selection coordinates.
 
----
-
-Notes & Caveats
----------------
+#### Notes & Caveats
 
 * **Selection behavior**
   * In the **Normal** variant, selection is tied to visible screen coordinates.
_AT_@ -114,10 +97,7 @@ Notes & Caveats
   duplicated or "ghosted". This is normal behavior caused by the shell reacting
   to `SIGWINCH` while st simultaneously reflows historical content.
 
----
-
-Configuration
--------------
+#### Configuration
 
 * **Scrollback size:** configurable via `scrollback_lines` in `config.h`
   (default: 5000).
_AT_@ -125,35 +105,29 @@ Configuration
 * **Key bindings:** defaults to `MouseWheel`, `Shift + PageUp/PageDown`, and
   `Shift + Home/End`.
 
----
-
-Example
--------
+#### Example
 
 The following example demonstrates resize reflow using a single line of
 colored blocks:
 
-    for i in {41..46}; do printf "\e[${i}m      "; done; echo -e "\e[0m"
+```sh
+for i in {41..46}; do printf "\e[${i}m      "; done; echo -e "\e[0m"
+```
 
-<img src="st-full.png" alt="Wide terminal with color blocks on one line" />
+**Wide terminal:** color blocks appear on a single line.
 
-<img src="st-shrunk.png" alt="Narrow terminal with color blocks reflowed across multiple lines" />
+![Wide terminal with color blocks on one line](st-full.png)
 
-In the narrow case, the original line is reflowed into multiple logical lines.
+**Narrow terminal:** the original line is reflowed into multiple lines.
 No content is clipped or lost; only wrapping changes.
 
+![Narrow terminal with color blocks reflowed across multiple lines](st-shrunk.png)
 
 Download
 --------
-
-* Normal: [st-scrollback-reflow-standalone-0.9.31.diff](st-scrollback-reflow-standalone-0.9.31.diff)
-* Extended: [st-scrollback-reflow-standalone-0.9.31-extended.diff](st-scrollback-reflow-standalone-0.9.31-extended.diff)
-
----
+* [st-scrollback-reflow-standalone-0.9.3.diff](st-scrollback-reflow-standalone-0.9.3.diff)
+* [st-scrollback-reflow-standalone-extended-0.9.3.diff](st-scrollback-reflow-standalone-extended-0.9.3.diff)
 
 Author
 ------
-
-* Loshmi <nloshmi_AT_gmail.com>
-* [GitHub](https://github.com/mnikic/st)
-
+* Loshmi - <nloshmi_AT_gmail.com>
diff --git a/st.suckless.org/patches/scrollback-reflow-standalone/st-scrollback-reflow-standalone-0.9.3.diff b/st.suckless.org/patches/scrollback-reflow-standalone/st-scrollback-reflow-standalone-0.9.3.diff
index f884c599..7394721a 100644
--- a/st.suckless.org/patches/scrollback-reflow-standalone/st-scrollback-reflow-standalone-0.9.3.diff
+++ b/st.suckless.org/patches/scrollback-reflow-standalone/st-scrollback-reflow-standalone-0.9.3.diff
_AT_@ -1,30 +1,43 @@
-From 6d7584cc382fd24e5da92baf3ac333b5dda12f3c Mon Sep 17 00:00:00 2001
+From 3f41a815d3a0527274b1e83238e821bd286c0905 Mon Sep 17 00:00:00 2001
 From: Milos Nikic <nikic.milos_AT_gmail.com>
-Date: Thu, 8 Jan 2026 22:04:25 -0800
-Subject: [PATCH] st: alternative scrollback using ring buffer and view offset
+Date: Thu, 15 Jan 2026 16:08:59 -0800
+Subject: [PATCH]     st: alternative scrollback using ring buffer and view
+ offset
 
-Implement scrollback as a fixed-size ring buffer and render history
-by offsetting the view instead of copying screen contents.
-Implement reflow of history and screen content on resizeif it is needed.
+    Implement scrollback as a fixed-size ring buffer and render history
+    by offsetting the view instead of copying screen contents.
+    Implement reflow of history and screen content on resize if it is needed.
 
-Tradeoffs / differences:
-- Scrollback is disabled on the alternate screen
-- Simpler model than the existing scrollback patch set
-- Mouse wheel scrolling enabled by default
-- When using vim, mouse movement will no longer move the cursor.
-- There can be visual artifacts if width of the window is shrank to the size smaller than the shell promp.
+    Tradeoffs / differences:
+    - Scrollback is disabled on the alternate screen
+    - Simpler model than the existing scrollback patch set
+    - Mouse wheel scrolling enabled by default
+    - Shift + page up/down and shift + end/home work as well.
+    - When using vim, mouse movement will no longer move the cursor.
+    - There can be visual artifacts if width of the window is shrank to the size smaller than the shell promp.
 ---
- config.def.h |   5 +
- st.c         | 710 ++++++++++++++++++++++++++++++++++++++++++++-------
+ config.def.h |   9 +
+ st.c         | 720 ++++++++++++++++++++++++++++++++++++++++++++-------
  st.h         |   5 +
  x.c          |  17 ++
- 4 files changed, 644 insertions(+), 93 deletions(-)
+ 4 files changed, 658 insertions(+), 93 deletions(-)
 
 diff --git a/config.def.h b/config.def.h
-index 2cd740a..a0b14e9 100644
+index 2cd740a..135a0b1 100644
 --- a/config.def.h
 +++ b/config.def.h
-_AT_@ -472,3 +472,8 @@ static char ascii_printable[] =
+_AT_@ -192,6 +192,10 @@ static Shortcut shortcuts[] = {
+ 	{ XK_ANY_MOD,           XK_Break,       sendbreak,      {.i =  0} },
+ 	{ ControlMask,          XK_Print,       toggleprinter,  {.i =  0} },
+ 	{ ShiftMask,            XK_Print,       printscreen,    {.i =  0} },
++	{ ShiftMask,            XK_Page_Up,     kscrollup,      {.i = -1} },
++	{ ShiftMask,            XK_Page_Down,   kscrolldown,    {.i = -1} },
++	{ ShiftMask,            XK_Home,        kscrollup,      {.i = 1000000} },
++	{ ShiftMask,            XK_End,         kscrolldown,    {.i = 1000000} },
+ 	{ XK_ANY_MOD,           XK_Print,       printsel,       {.i =  0} },
+ 	{ TERMMOD,              XK_Prior,       zoom,           {.f = +1} },
+ 	{ TERMMOD,              XK_Next,        zoom,           {.f = -1} },
+_AT_@ -472,3 +476,8 @@ static char ascii_printable[] =
  	" !\"#$%&'()*+,-./0123456789:;<=>?"
  	"_AT_ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_"
  	"`abcdefghijklmnopqrstuvwxyz{|}~";
_AT_@ -34,7 +47,7 @@ index 2cd740a..a0b14e9 100644
 + */
 +unsigned int scrollback_lines = 5000;
 diff --git a/st.c b/st.c
-index e55e7b3..3f6b4a8 100644
+index e55e7b3..3b0803f 100644
 --- a/st.c
 +++ b/st.c
 _AT_@ -5,6 +5,7 @@
_AT_@ -712,7 +725,7 @@ index e55e7b3..3f6b4a8 100644
  	if (bp != end || bp->u != ' ') {
  		for ( ; bp <= end; ++bp)
  			tprinter(buf, utf8encode(bp->u, buf));
-_AT_@ -2163,6 +2601,36 @@ tdeftran(char ascii)
+_AT_@ -2163,6 +2601,46 @@ tdeftran(char ascii)
  	}
  }
  
_AT_@ -726,7 +739,6 @@ index e55e7b3..3f6b4a8 100644
 +	sb.view_offset += arg->i;
 +	LIMIT(sb.view_offset, 0, sb.len);
 +	newstart = sb_view_start();
-+
 +	selscrollback(oldstart - newstart);
 +	redraw();
 +}
_AT_@ -736,20 +748,31 @@ index e55e7b3..3f6b4a8 100644
 +{
 +	Arg a;
 +
-+	a.i = -arg->i;
++	if (arg->i < 0)
++		a.i = -term.row;
++	else
++		a.i = -arg->i;
++
 +	kscroll(&a);
 +}
 +
 +void
 +kscrollup(const Arg *arg)
 +{
-+	kscroll(arg);
++	Arg a;
++
++	if (arg->i < 0)
++		a.i = term.row;
++	else
++		a.i = arg->i;
++
++	kscroll(&a);
 +}
 +
  void
  tdectest(char c)
  {
-_AT_@ -2569,83 +3037,136 @@ twrite(const char *buf, int buflen, int show_ctrl)
+_AT_@ -2569,83 +3047,136 @@ twrite(const char *buf, int buflen, int show_ctrl)
  void
  tresize(int col, int row)
  {
_AT_@ -946,7 +969,7 @@ index e55e7b3..3f6b4a8 100644
  }
  
  void
-_AT_@ -2659,12 +3180,13 @@ drawregion(int x1, int y1, int x2, int y2)
+_AT_@ -2659,12 +3190,13 @@ drawregion(int x1, int y1, int x2, int y2)
  {
  	int y;
  
_AT_@ -962,7 +985,7 @@ index e55e7b3..3f6b4a8 100644
  	}
  }
  
-_AT_@ -2685,10 +3207,12 @@ draw(void)
+_AT_@ -2685,10 +3217,12 @@ draw(void)
  		cx--;
  
  	drawregion(0, 0, term.col, term.row);
diff --git a/st.suckless.org/patches/scrollback-reflow-standalone/st-scrollback-reflow-standalone-0.9.31.diff b/st.suckless.org/patches/scrollback-reflow-standalone/st-scrollback-reflow-standalone-0.9.31.diff
deleted file mode 100644
index 7394721a..00000000
--- a/st.suckless.org/patches/scrollback-reflow-standalone/st-scrollback-reflow-standalone-0.9.31.diff
+++ /dev/null
_AT_@ -1,1062 +0,0 @@
-From 3f41a815d3a0527274b1e83238e821bd286c0905 Mon Sep 17 00:00:00 2001
-From: Milos Nikic <nikic.milos_AT_gmail.com>
-Date: Thu, 15 Jan 2026 16:08:59 -0800
-Subject: [PATCH]     st: alternative scrollback using ring buffer and view
- offset
-
-    Implement scrollback as a fixed-size ring buffer and render history
-    by offsetting the view instead of copying screen contents.
-    Implement reflow of history and screen content on resize if it is needed.
-
-    Tradeoffs / differences:
-    - Scrollback is disabled on the alternate screen
-    - Simpler model than the existing scrollback patch set
-    - Mouse wheel scrolling enabled by default
-    - Shift + page up/down and shift + end/home work as well.
-    - When using vim, mouse movement will no longer move the cursor.
-    - There can be visual artifacts if width of the window is shrank to the size smaller than the shell promp.
----
- config.def.h |   9 +
- st.c         | 720 ++++++++++++++++++++++++++++++++++++++++++++-------
- st.h         |   5 +
- x.c          |  17 ++
- 4 files changed, 658 insertions(+), 93 deletions(-)
-
-diff --git a/config.def.h b/config.def.h
-index 2cd740a..135a0b1 100644
---- a/config.def.h
-+++ b/config.def.h
-_AT_@ -192,6 +192,10 @@ static Shortcut shortcuts[] = {
- 	{ XK_ANY_MOD,           XK_Break,       sendbreak,      {.i =  0} },
- 	{ ControlMask,          XK_Print,       toggleprinter,  {.i =  0} },
- 	{ ShiftMask,            XK_Print,       printscreen,    {.i =  0} },
-+	{ ShiftMask,            XK_Page_Up,     kscrollup,      {.i = -1} },
-+	{ ShiftMask,            XK_Page_Down,   kscrolldown,    {.i = -1} },
-+	{ ShiftMask,            XK_Home,        kscrollup,      {.i = 1000000} },
-+	{ ShiftMask,            XK_End,         kscrolldown,    {.i = 1000000} },
- 	{ XK_ANY_MOD,           XK_Print,       printsel,       {.i =  0} },
- 	{ TERMMOD,              XK_Prior,       zoom,           {.f = +1} },
- 	{ TERMMOD,              XK_Next,        zoom,           {.f = -1} },
-_AT_@ -472,3 +476,8 @@ static char ascii_printable[] =
- 	" !\"#$%&'()*+,-./0123456789:;<=>?"
- 	"_AT_ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_"
- 	"`abcdefghijklmnopqrstuvwxyz{|}~";
-+
-+/*
-+ * The amount of lines scrollback can hold before it wraps around.
-+ */
-+unsigned int scrollback_lines = 5000;
-diff --git a/st.c b/st.c
-index e55e7b3..3b0803f 100644
---- a/st.c
-+++ b/st.c
-_AT_@ -5,6 +5,7 @@
- #include <limits.h>
- #include <pwd.h>
- #include <stdarg.h>
-+#include <stdint.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
-_AT_@ -178,7 +179,7 @@ static void tdeletechar(int);
- static void tdeleteline(int);
- static void tinsertblank(int);
- static void tinsertblankline(int);
--static int tlinelen(int);
-+static int tlinelen(Line);
- static void tmoveto(int, int);
- static void tmoveato(int, int);
- static void tnewline(int);
-_AT_@ -232,6 +233,379 @@ static const uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};
- static const Rune utfmin[UTF_SIZ + 1] = {       0,    0,  0x80,  0x800,  0x10000};
- static const Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
- 
-+typedef struct
-+{
-+	Line *buf;       /* ring of Line pointers */
-+	int cap;         /* max number of lines */
-+	int len;         /* current number of valid lines (<= cap) */
-+	int head;        /* physical index of logical oldest (valid when len>0) */
-+	uint64_t base;   /* Can overflow in the extreme */
-+	/*
-+	 * max_width tracks the widest line ever pushed to scrollback.
-+	 * It may be conservative (stale) if that line has since been
-+	 * evicted from the ring buffer, which is acceptable - it just
-+	 * means we might reflow when not strictly necessary, which is
-+	 * better than skipping a needed reflow.
-+	 */
-+	int max_width;
-+	int view_offset; /* 0 means live screen */
-+} Scrollback;
-+
-+static Scrollback sb;
-+
-+static int
-+sb_phys_index(int logical_idx)
-+{
-+	/* logical_idx: 0..sb.len-1 (0 = oldest) */
-+	return (sb.head + logical_idx) % sb.cap;
-+}
-+
-+static Line
-+lineclone(Line src)
-+{
-+	Line dst;
-+
-+	if (!src)
-+		return NULL;
-+
-+	dst = xmalloc(term.col * sizeof(Glyph));
-+	memcpy(dst, src, term.col * sizeof(Glyph));
-+	return dst;
-+}
-+
-+static void
-+sb_init(int lines)
-+{
-+	int i;
-+
-+	sb.buf  = xmalloc(sizeof(Line) * lines);
-+	sb.cap  = lines;
-+	sb.len  = 0;
-+	sb.head = 0;
-+	sb.base = 0;
-+	for (i = 0; i < sb.cap; i++)
-+		sb.buf[i] = NULL;
-+
-+	sb.view_offset = 0;
-+	sb.max_width = 0;
-+}
-+
-+/* Push one screen line into scrollback.
-+ * Overwrites oldest when full (ring buffer).
-+ */
-+static void
-+sb_push(Line line)
-+{
-+	Line copy;
-+	int tail;
-+	int width;
-+
-+	if (sb.cap <= 0)
-+		return;
-+
-+	copy = lineclone(line);
-+
-+	if (sb.len < sb.cap) {
-+		tail = sb_phys_index(sb.len);
-+		sb.buf[tail] = copy;
-+		sb.len++;
-+	} else {
-+		/* We might've just evicted the widest line... */
-+		free(sb.buf[sb.head]);
-+		sb.buf[sb.head] = copy;
-+		sb.head = (sb.head + 1) % sb.cap;
-+		sb.base++;
-+	}
-+	width = tlinelen(copy);
-+	/* ...so max_width might be stale. */
-+	if (width > sb.max_width)
-+		sb.max_width = width;
-+}
-+
-+static Line
-+sb_get(int idx)
-+{
-+	/* idx is logical: 0..sb.len-1 */
-+	if (idx < 0 || idx >= sb.len)
-+		return NULL;
-+	return sb.buf[sb_phys_index(idx)];
-+}
-+
-+static void
-+sb_clear(void)
-+{
-+	int i;
-+	int p;
-+
-+	if (!sb.buf)
-+		return;
-+
-+	for (i = 0; i < sb.len; i++) {
-+		p = sb_phys_index(i);
-+		if (sb.buf[p]) {
-+			free(sb.buf[p]);
-+			sb.buf[p] = NULL;
-+		}
-+	}
-+
-+	sb.len = 0;
-+	sb.head = 0;
-+	sb.base = 0;
-+	sb.view_offset = 0;
-+	sb.max_width = 0;
-+}
-+
-+/*
-+ * Reflows the scrollback buffer to fit a new terminal width.
-+ *
-+ * The algorithm works in three steps:
-+ * 1) Unwrap: It iterates through the existing history, joining physical lines
-+ * marked with ATTR_WRAP into a single continuous 'logical' line.
-+ * 2) Reflow: It slices this logical line into new chunks of size 'col'.
-+ * - New wrap flags are applied where the text exceeds the new width.
-+ * - Trailing spaces are trimmed to prevent ghost padding.
-+ * 3) Rebuild: The new lines are pushed into a fresh ring buffer.
-+ * - Uses O(1) ring insertion (updating head/tail) to avoid expensive
-+ * memmoves during resize, but it is still O(N) where N is the existing
-+ * history.
-+ *
-+ * Note: During reflow we reset sb to match the rebuilt buffer
-+ * (head, base and len might change).
-+ */
-+static void
-+sb_resize(int col)
-+{
-+	Line *new_buf;
-+	int i, j;
-+	int new_len, logical_cap, logical_len, is_wrapped, cursor;
-+	int copy_width, tail, current_width;
-+	Line logical, line, nl;
-+	uint64_t new_base = 0;
-+	int new_head = 0;
-+	int new_max_width = 0;
-+	Glyph *g;
-+
-+	new_len = 0;
-+
-+	if (sb.len == 0)
-+		return;
-+
-+	new_buf = xmalloc(sizeof(Line) * sb.cap);
-+	for (i = 0; i < sb.cap; i++)
-+		new_buf[i] = NULL;
-+
-+	logical_cap = term.col * 2;
-+	logical = xmalloc(logical_cap * sizeof(Glyph));
-+	logical_len = 0;
-+
-+	for (i = 0; i < sb.len; i++) {
-+		/* Unwrap: Accumulate physical lines into one logical line. */
-+		line = sb_get(i);
-+		is_wrapped = (line[term.col - 1].mode & ATTR_WRAP);
-+		if (logical_len + term.col > logical_cap) {
-+			logical_cap *= 2;
-+			logical = xrealloc(logical, logical_cap * sizeof(Glyph));
-+		}
-+
-+		memcpy(logical + logical_len, line, term.col * sizeof(Glyph));
-+		for (j = 0; j < term.col; j++) {
-+			logical[logical_len + j].mode &= ~ATTR_WRAP;
-+		}
-+		logical_len += term.col;
-+		/* If the line was wrapped, continue accumulating before reflowing. */
-+		if (is_wrapped) {
-+			continue;
-+		}
-+		/* Trim trailing spaces from the fully unwrapped line. */
-+		while (logical_len > 0) {
-+			g = &logical[logical_len - 1];
-+			if (g->u == ' ' && g->bg == defaultbg
-+					&& (g->mode & ATTR_BOLD) == 0) {
-+				logical_len--;
-+			} else {
-+				break;
-+			}
-+		}
-+		if (logical_len == 0)
-+			logical_len = 1;
-+
-+		/* Reflow: Split the logical line into new chunks. */
-+		cursor = 0;
-+		while (cursor < logical_len) {
-+			nl = xmalloc(col * sizeof(Glyph));
-+			for (j = 0; j < col; j++) {
-+				nl[j].fg = defaultfg;
-+				nl[j].bg = defaultbg;
-+				nl[j].mode = 0;
-+				nl[j].u = ' ';
-+			}
-+
-+			copy_width = logical_len - cursor;
-+			if (copy_width > col)
-+				copy_width = col;
-+
-+			memcpy(nl, logical + cursor, copy_width * sizeof(Glyph));
-+
-+			for (j = 0; j < copy_width; j++) {
-+				nl[j].mode &= ~ATTR_WRAP;
-+			}
-+
-+			if (cursor + copy_width < logical_len) {
-+				nl[col - 1].mode |= ATTR_WRAP;
-+			} else {
-+				nl[col - 1].mode &= ~ATTR_WRAP;
-+			}
-+
-+			/* Rebuild: Push new lines into the ring buffer. */
-+			if (new_len < sb.cap) {
-+				tail = (new_head + new_len) % sb.cap;
-+				new_buf[tail] = nl;
-+				new_len++;
-+			} else {
-+				free(new_buf[new_head]);
-+				new_buf[new_head] = nl;
-+				new_head = (new_head + 1) % sb.cap;
-+				new_base++;
-+			}
-+			current_width = (cursor + copy_width < logical_len) ? col : copy_width;
-+			if (current_width > new_max_width)
-+				new_max_width = current_width;
-+			cursor += copy_width;
-+		}
-+		logical_len = 0;
-+	}
-+	free(logical);
-+	sb_clear();
-+	free(sb.buf);
-+	sb.buf = new_buf;
-+	sb.len = new_len;
-+	sb.head = new_head;
-+	sb.base = new_base;
-+	sb.view_offset = 0;
-+	sb.max_width = new_max_width;
-+}
-+
-+static void
-+sb_pop_screen(int loaded, int new_cols)
-+{
-+	int i, p;
-+	int start_logical;
-+	Line line;
-+
-+	loaded = MIN(loaded, sb.len);
-+	start_logical = sb.len - loaded;
-+	new_cols = MIN(new_cols, term.col);
-+	for (i = 0; i < loaded; i++) {
-+		p = sb_phys_index(start_logical + i);
-+		line = sb.buf[p];
-+
-+		memcpy(term.line[i], line, new_cols * sizeof(Glyph));
-+
-+		free(line);
-+		sb.buf[p] = NULL;
-+	}
-+
-+	sb.len -= loaded;
-+}
-+
-+static uint64_t
-+sb_view_start(void)
-+{
-+	return sb.base + sb.len - sb.view_offset;
-+}
-+
-+static void
-+sb_view_changed(void)
-+{
-+	if (!term.dirty || term.row <= 0)
-+		return;
-+	tfulldirt();
-+}
-+
-+static void
-+selscrollback(int delta)
-+{
-+	if (delta == 0)
-+		return;
-+
-+	if (sel.ob.x == -1 || sel.mode == SEL_EMPTY)
-+		return;
-+
-+	if (sel.alt != IS_SET(MODE_ALTSCREEN))
-+		return;
-+
-+	sel.nb.y += delta;
-+	sel.ne.y += delta;
-+	sel.ob.y += delta;
-+	sel.oe.y += delta;
-+
-+	if (sel.ne.y < 0 || sel.nb.y >= term.row)
-+		selclear();
-+
-+	sb_view_changed();
-+}
-+
-+static Line
-+emptyline(void)
-+{
-+	static Line empty;
-+	static int empty_cols;
-+	int i = 0;
-+
-+	if (empty_cols != term.col) {
-+		free(empty);
-+		empty = xmalloc(term.col * sizeof(Glyph));
-+		empty_cols = term.col;
-+	}
-+
-+	for (i = 0; i < term.col; i++) {
-+		empty[i] = term.c.attr;
-+		empty[i].u = ' ';
-+		empty[i].mode = 0;
-+	}
-+	return empty;
-+}
-+
-+static Line
-+renderline(int y)
-+{
-+	int start, v;
-+
-+	if (sb.view_offset <= 0)
-+		return term.line[y];
-+
-+	start = sb.len - sb.view_offset; /* can be negative */
-+	v = start + y;
-+
-+	if (v < 0)
-+		return emptyline();
-+
-+	if (v < sb.len)
-+		return sb_get(v);
-+
-+	/* past scrollback -> into current screen */
-+	v -= sb.len;
-+	if (v >= 0 && v < term.row)
-+		return term.line[v];
-+
-+	return emptyline();
-+}
-+
-+static void
-+sb_reset_on_clear(void)
-+{
-+	sb_clear();
-+	sb_view_changed();
-+	if (sel.ob.x != -1 && term.row > 0)
-+		selclear();
-+}
-+
-+int
-+tisaltscreen(void)
-+{
-+	return IS_SET(MODE_ALTSCREEN);
-+}
-+
- ssize_t
- xwrite(int fd, const char *s, size_t len)
- {
-_AT_@ -404,20 +778,23 @@ selinit(void)
- 	sel.ob.x = -1;
- }
- 
--int
--tlinelen(int y)
-+static int
-+tlinelen(Line line)
- {
- 	int i = term.col;
--
--	if (term.line[y][i - 1].mode & ATTR_WRAP)
-+	if (line[i - 1].mode & ATTR_WRAP)
- 		return i;
--
--	while (i > 0 && term.line[y][i - 1].u == ' ')
-+	while (i > 0 && line[i - 1].u == ' ')
- 		--i;
--
- 	return i;
- }
- 
-+static int
-+tlinelen_render(int y)
-+{
-+	return tlinelen(renderline(y));
-+}
-+
- void
- selstart(int col, int row, int snap)
- {
-_AT_@ -485,10 +862,10 @@ selnormalize(void)
- 	/* expand selection over line breaks */
- 	if (sel.type == SEL_RECTANGULAR)
- 		return;
--	i = tlinelen(sel.nb.y);
-+	i = tlinelen_render(sel.nb.y);
- 	if (i < sel.nb.x)
- 		sel.nb.x = i;
--	if (tlinelen(sel.ne.y) <= sel.ne.x)
-+	if (tlinelen_render(sel.ne.y) <= sel.ne.x)
- 		sel.ne.x = term.col - 1;
- }
- 
-_AT_@ -514,6 +891,7 @@ selsnap(int *x, int *y, int direction)
- 	int newx, newy, xt, yt;
- 	int delim, prevdelim;
- 	const Glyph *gp, *prevgp;
-+	Line line;
- 
- 	switch (sel.snap) {
- 	case SNAP_WORD:
-_AT_@ -521,7 +899,7 @@ selsnap(int *x, int *y, int direction)
- 		 * Snap around if the word wraps around at the end or
- 		 * beginning of a line.
- 		 */
--		prevgp = &term.line[*y][*x];
-+		prevgp = &renderline(*y)[*x];
- 		prevdelim = ISDELIM(prevgp->u);
- 		for (;;) {
- 			newx = *x + direction;
-_AT_@ -536,14 +914,15 @@ selsnap(int *x, int *y, int direction)
- 					yt = *y, xt = *x;
- 				else
- 					yt = newy, xt = newx;
--				if (!(term.line[yt][xt].mode & ATTR_WRAP))
-+				line = renderline(yt);
-+				if (!(line[xt].mode & ATTR_WRAP))
- 					break;
- 			}
- 
--			if (newx >= tlinelen(newy))
-+			if (newx >= tlinelen_render(newy))
- 				break;
- 
--			gp = &term.line[newy][newx];
-+			gp = &renderline(newy)[newx];
- 			delim = ISDELIM(gp->u);
- 			if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim
- 					|| (delim && gp->u != prevgp->u)))
-_AT_@ -564,14 +943,14 @@ selsnap(int *x, int *y, int direction)
- 		*x = (direction < 0) ? 0 : term.col - 1;
- 		if (direction < 0) {
- 			for (; *y > 0; *y += direction) {
--				if (!(term.line[*y-1][term.col-1].mode
-+				if (!(renderline(*y-1)[term.col-1].mode
- 						& ATTR_WRAP)) {
- 					break;
- 				}
- 			}
- 		} else if (direction > 0) {
- 			for (; *y < term.row-1; *y += direction) {
--				if (!(term.line[*y][term.col-1].mode
-+				if (!(renderline(*y)[term.col-1].mode
- 						& ATTR_WRAP)) {
- 					break;
- 				}
-_AT_@ -585,8 +964,9 @@ char *
- getsel(void)
- {
- 	char *str, *ptr;
--	int y, bufsize, lastx, linelen;
-+	int y, bufsize, lastx, linelen, end_idx, insert_newline, is_wrapped;
- 	const Glyph *gp, *last;
-+	Line line;
- 
- 	if (sel.ob.x == -1)
- 		return NULL;
-_AT_@ -596,29 +976,33 @@ getsel(void)
- 
- 	/* append every set & selected glyph to the selection */
- 	for (y = sel.nb.y; y <= sel.ne.y; y++) {
--		if ((linelen = tlinelen(y)) == 0) {
-+		line = renderline(y);
-+		linelen = tlinelen_render(y);
-+
-+		if (linelen == 0) {
- 			*ptr++ = '
';
- 			continue;
- 		}
- 
- 		if (sel.type == SEL_RECTANGULAR) {
--			gp = &term.line[y][sel.nb.x];
-+			gp = &line[sel.nb.x];
- 			lastx = sel.ne.x;
- 		} else {
--			gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0];
-+			gp = &line[sel.nb.y == y ? sel.nb.x : 0];
- 			lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1;
- 		}
--		last = &term.line[y][MIN(lastx, linelen-1)];
--		while (last >= gp && last->u == ' ')
-+		end_idx = MIN(lastx, linelen-1);
-+		is_wrapped = (line[end_idx].mode & ATTR_WRAP) != 0;
-+		last = &line[end_idx];
-+		while (last >= gp && last->u == ' ') {
- 			--last;
-+		}
- 
- 		for ( ; gp <= last; ++gp) {
- 			if (gp->mode & ATTR_WDUMMY)
- 				continue;
--
- 			ptr += utf8encode(gp->u, ptr);
- 		}
--
- 		/*
- 		 * Copy and pasting of line endings is inconsistent
- 		 * in the inconsistent terminal and GUI world.
-_AT_@ -628,8 +1012,13 @@ getsel(void)
- 		 * st.
- 		 * FIXME: Fix the computer world.
- 		 */
-+		insert_newline = 0;
- 		if ((y < sel.ne.y || lastx >= linelen) &&
--		    (!(last->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR))
-+			(!is_wrapped || sel.type == SEL_RECTANGULAR)) {
-+			insert_newline = 1;
-+		}
-+
-+		if (insert_newline)
- 			*ptr++ = '
';
- 	}
- 	*ptr = 0;
-_AT_@ -845,6 +1234,12 @@ ttywrite(const char *s, size_t n, int may_echo)
- {
- 	const char *next;
- 
-+	if (sb.view_offset > 0) {
-+		selclear();
-+		sb.view_offset = 0;
-+		sb_view_changed();
-+	}
-+
- 	if (may_echo && IS_SET(MODE_ECHO))
- 		twrite(s, n, 1);
- 
-_AT_@ -965,6 +1360,8 @@ tsetdirt(int top, int bot)
- {
- 	int i;
- 
-+	if (term.row < 1)
-+		return;
- 	LIMIT(top, 0, term.row-1);
- 	LIMIT(bot, 0, term.row-1);
- 
-_AT_@ -1030,15 +1427,21 @@ treset(void)
- 	for (i = 0; i < 2; i++) {
- 		tmoveto(0, 0);
- 		tcursor(CURSOR_SAVE);
--		tclearregion(0, 0, term.col-1, term.row-1);
-+		if (term.col > 0 && term.row > 0 && term.line > 0)
-+			tclearregion(0, 0, term.col-1, term.row-1);
- 		tswapscreen();
- 	}
-+	sb_clear();
-+	if (sel.ob.x != -1 && term.row > 0)
-+		selclear();
- }
- 
-+
- void
- tnew(int col, int row)
- {
- 	term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } };
-+	sb_init(scrollback_lines);
- 	tresize(col, row);
- 	treset();
- }
-_AT_@ -1078,10 +1481,37 @@ void
- tscrollup(int orig, int n)
- {
- 	int i;
-+	uint64_t newstart;
-+	uint64_t oldstart;
-+
-+	int attop;
- 	Line temp;
- 
-+	oldstart = sb_view_start();
- 	LIMIT(n, 0, term.bot-orig+1);
- 
-+	if (!IS_SET(MODE_ALTSCREEN) && orig == term.top) {
-+		/* At top of history only if history exists */
-+		attop = (sb.len != 0 && sb.view_offset == sb.len);
-+
-+		if (sb.view_offset > 0 && !attop)
-+			sb.view_offset += n;
-+
-+		for (i = 0; i < n; i++)
-+			sb_push(term.line[orig + i]);
-+
-+		/* if at the top, keep me there */
-+		if (attop)
-+			sb.view_offset = sb.len;
-+		/* otherwise clamp me */
-+		else if (sb.view_offset > sb.len)
-+			sb.view_offset = sb.len;
-+	}
-+
-+	newstart = sb_view_start();
-+	if (sb.view_offset > 0)
-+		selscrollback(oldstart - newstart);
-+
- 	tclearregion(0, orig, term.col-1, orig+n-1);
- 	tsetdirt(orig+n, term.bot);
- 
-_AT_@ -1097,6 +1527,8 @@ tscrollup(int orig, int n)
- void
- selscroll(int orig, int n)
- {
-+	if (sb.view_offset != 0)
-+		return;
- 	if (sel.ob.x == -1 || sel.alt != IS_SET(MODE_ALTSCREEN))
- 		return;
- 
-_AT_@ -1717,6 +2149,12 @@ csihandle(void)
- 			break;
- 		case 2: /* all */
- 			tclearregion(0, 0, term.col-1, term.row-1);
-+			if (!IS_SET(MODE_ALTSCREEN))
-+				sb_reset_on_clear();
-+			break;
-+		case 3:
-+			if (!IS_SET(MODE_ALTSCREEN))
-+				sb_reset_on_clear();
- 			break;
- 		default:
- 			goto unknown;
-_AT_@ -2106,7 +2544,7 @@ tdumpline(int n)
- 	const Glyph *bp, *end;
- 
- 	bp = &term.line[n][0];
--	end = &bp[MIN(tlinelen(n), term.col) - 1];
-+	end = &bp[MIN(tlinelen_render(n), term.col) - 1];
- 	if (bp != end || bp->u != ' ') {
- 		for ( ; bp <= end; ++bp)
- 			tprinter(buf, utf8encode(bp->u, buf));
-_AT_@ -2163,6 +2601,46 @@ tdeftran(char ascii)
- 	}
- }
- 
-+static void
-+kscroll(const Arg *arg)
-+{
-+	uint64_t oldstart;
-+	uint64_t newstart;
-+
-+	oldstart = sb_view_start();
-+	sb.view_offset += arg->i;
-+	LIMIT(sb.view_offset, 0, sb.len);
-+	newstart = sb_view_start();
-+	selscrollback(oldstart - newstart);
-+	redraw();
-+}
-+
-+void
-+kscrolldown(const Arg *arg)
-+{
-+	Arg a;
-+
-+	if (arg->i < 0)
-+		a.i = -term.row;
-+	else
-+		a.i = -arg->i;
-+
-+	kscroll(&a);
-+}
-+
-+void
-+kscrollup(const Arg *arg)
-+{
-+	Arg a;
-+
-+	if (arg->i < 0)
-+		a.i = term.row;
-+	else
-+		a.i = arg->i;
-+
-+	kscroll(&a);
-+}
-+
- void
- tdectest(char c)
- {
-_AT_@ -2569,83 +3047,136 @@ twrite(const char *buf, int buflen, int show_ctrl)
- void
- tresize(int col, int row)
- {
--	int i;
-+	int i, j;
-+	int min_limit;
- 	int minrow = MIN(row, term.row);
--	int mincol = MIN(col, term.col);
--	int *bp;
--	TCursor c;
-+	int old_row = term.row;
-+	int old_col = term.col;
-+	int save_end = 0; /* Track effective pushed height */
-+	int loaded = 0;
-+	int pop_width = 0;
-+	int needs_reflow = 0;
-+	int is_alt = IS_SET(MODE_ALTSCREEN);
-+	Line *tmp;
- 
- 	if (col < 1 || row < 1) {
- 		fprintf(stderr,
--		        "tresize: error resizing to %dx%d
", col, row);
-+			"tresize: error resizing to %dx%d
", col, row);
- 		return;
- 	}
- 
--	/*
--	 * slide screen to keep cursor where we expect it -
--	 * tscrollup would work here, but we can optimize to
--	 * memmove because we're freeing the earlier lines
--	 */
--	for (i = 0; i <= term.c.y - row; i++) {
--		free(term.line[i]);
--		free(term.alt[i]);
--	}
--	/* ensure that both src and dst are not NULL */
--	if (i > 0) {
--		memmove(term.line, term.line + i, row * sizeof(Line));
--		memmove(term.alt, term.alt + i, row * sizeof(Line));
--	}
--	for (i += row; i < term.row; i++) {
--		free(term.line[i]);
--		free(term.alt[i]);
-+	/* Operate on the currently visible screen buffer. */
-+	if (is_alt) {
-+		tmp = term.line;
-+		term.line = term.alt;
-+		term.alt = tmp;
- 	}
- 
--	/* resize to new height */
--	term.line = xrealloc(term.line, row * sizeof(Line));
--	term.alt  = xrealloc(term.alt,  row * sizeof(Line));
--	term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty));
--	term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs));
-+	save_end = term.row;
-+	if (term.row != 0 && term.col != 0) {
-+		if (!is_alt && term.c.y > 0 && term.c.y < term.row) {
-+			term.line[term.c.y - 1][term.col - 1].mode &= ~ATTR_WRAP;
-+		}
-+		min_limit = is_alt ? 0 : term.c.y;
- 
--	/* resize each row to new width, zero-pad if needed */
--	for (i = 0; i < minrow; i++) {
--		term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph));
--		term.alt[i]  = xrealloc(term.alt[i],  col * sizeof(Glyph));
--	}
-+		for (i = term.row - 1; i > min_limit; i--) {
-+			if (tlinelen(term.line[i]) > 0)
-+				break;
-+		}
-+		save_end = i + 1;
- 
--	/* allocate any new rows */
--	for (/* i = minrow */; i < row; i++) {
--		term.line[i] = xmalloc(col * sizeof(Glyph));
--		term.alt[i] = xmalloc(col * sizeof(Glyph));
-+		for (i = 0; i < save_end; i++) {
-+			sb_push(term.line[i]);
-+		}
-+		/* Optimization: Only reflow if content doesn't fit in new width.
-+		 * This avoids expensive reflow operations when resizing doesn't
-+		 * affect line wrapping (e.g., when terminal is wide enough). */
-+		if (col > term.col) {
-+			/* Growing: Only reflow if history was wrapped at old width */
-+			needs_reflow = sb.max_width >= term.col;
-+		} else if (col < term.col) {
-+			/* Shrinking: Only reflow if content is wider than new width. */
-+			if (sb.max_width > col)
-+				needs_reflow = 1;
-+		}
-+		if (needs_reflow) {
-+			sb_resize(col);
-+		} else {
-+			/* If we don't reflow, we still need to reset the view 
-+			 * because sb_pop_screen() might change the history length. */
-+			sb.view_offset = 0;
-+		}
- 	}
--	if (col > term.col) {
--		bp = term.tabs + term.col;
- 
--		memset(bp, 0, sizeof(*term.tabs) * (col - term.col));
--		while (--bp > term.tabs && !*bp)
--			/* nothing */ ;
--		for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces)
--			*bp = 1;
--	}
--	/* update terminal size */
-+		if (term.line) {
-+			for (i = 0; i < term.row; i++) {
-+				free(term.line[i]);
-+				free(term.alt[i]);
-+			}
-+			free(term.line);
-+			free(term.alt);
-+			free(term.dirty);
-+			free(term.tabs);
-+		}
-+
- 	term.col = col;
- 	term.row = row;
--	/* reset scrolling region */
--	tsetscroll(0, row-1);
--	/* make use of the LIMIT in tmoveto */
--	tmoveto(term.c.x, term.c.y);
--	/* Clearing both screens (it makes dirty all lines) */
--	c = term.c;
--	for (i = 0; i < 2; i++) {
--		if (mincol < col && 0 < minrow) {
--			tclearregion(mincol, 0, col - 1, minrow - 1);
--		}
--		if (0 < col && minrow < row) {
--			tclearregion(0, minrow, col - 1, row - 1);
-+
-+	term.line  = xmalloc(term.row * sizeof(Line));
-+	term.alt   = xmalloc(term.row * sizeof(Line));
-+	term.dirty = xmalloc(term.row * sizeof(int));
-+	term.tabs  = xmalloc(term.col * sizeof(*term.tabs));
-+
-+	for (i = 0; i < term.row; i++) {
-+		term.line[i] = xmalloc(term.col * sizeof(Glyph));
-+		term.alt[i]  = xmalloc(term.col * sizeof(Glyph));
-+		term.dirty[i] = 1;
-+
-+		for (j = 0; j < term.col; j++) {
-+			term.line[i][j] = term.c.attr;
-+			term.line[i][j].u = ' ';
-+			term.line[i][j].mode = 0;
-+
-+			term.alt[i][j] = term.c.attr;
-+			term.alt[i][j].u = ' ';
-+			term.alt[i][j].mode = 0;
- 		}
--		tswapscreen();
--		tcursor(CURSOR_LOAD);
- 	}
--	term.c = c;
-+
-+	memset(term.tabs, 0, term.col * sizeof(*term.tabs));
-+	for (i = 8; i < term.col; i += 8)
-+		term.tabs[i] = 1;
-+
-+	tsetscroll(0, term.row - 1);
-+
-+	if (minrow > 0) {
-+		loaded = MIN(sb.len, term.row);
-+		pop_width = needs_reflow ? col : MIN(col, old_col);
-+		sb_pop_screen(loaded, pop_width);
-+	}
-+	if (is_alt) {
-+		tmp = term.line;
-+		term.line = term.alt;
-+		term.alt = tmp;
-+	}
-+	if (!is_alt && old_row > 0) {
-+		term.c.y += (loaded - save_end);
-+	}
-+	if (term.c.y >= term.row) {
-+		term.c.y = term.row - 1;
-+	}
-+	if (term.c.x >= term.col) {
-+		term.c.x = term.col - 1;
-+	}
-+	if (term.c.y < 0) {
-+		term.c.y = 0;
-+	}
-+	if (term.c.x < 0) {
-+		term.c.x = 0;
-+	}
-+
-+	tfulldirt();
-+	sb_view_changed();
- }
- 
- void
-_AT_@ -2659,12 +3190,13 @@ drawregion(int x1, int y1, int x2, int y2)
- {
- 	int y;
- 
-+	Line line;
- 	for (y = y1; y < y2; y++) {
- 		if (!term.dirty[y])
- 			continue;
--
- 		term.dirty[y] = 0;
--		xdrawline(term.line[y], x1, y, x2);
-+		line = renderline(y);
-+		xdrawline(line, x1, y, x2);
- 	}
- }
- 
-_AT_@ -2685,10 +3217,12 @@ draw(void)
- 		cx--;
- 
- 	drawregion(0, 0, term.col, term.row);
--	xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
--			term.ocx, term.ocy, term.line[term.ocy][term.ocx]);
--	term.ocx = cx;
--	term.ocy = term.c.y;
-+	if (sb.view_offset == 0) {
-+		xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
-+		            term.ocx, term.ocy, term.line[term.ocy][term.ocx]);
-+		term.ocx = cx;
-+		term.ocy = term.c.y;
-+	}
- 	xfinishdraw();
- 	if (ocx != term.ocx || ocy != term.ocy)
- 		xximspot(term.ocx, term.ocy);
-diff --git a/st.h b/st.h
-index fd3b0d8..151d0c6 100644
---- a/st.h
-+++ b/st.h
-_AT_@ -86,6 +86,7 @@ void printsel(const Arg *);
- void sendbreak(const Arg *);
- void toggleprinter(const Arg *);
- 
-+int tisaltscreen(void);
- int tattrset(int);
- void tnew(int, int);
- void tresize(int, int);
-_AT_@ -111,6 +112,9 @@ void *xmalloc(size_t);
- void *xrealloc(void *, size_t);
- char *xstrdup(const char *);
- 
-+void kscrollup(const Arg *arg);
-+void kscrolldown(const Arg *arg);
-+
- /* config.h globals */
- extern char *utmp;
- extern char *scroll;
-_AT_@ -124,3 +128,4 @@ extern unsigned int tabspaces;
- extern unsigned int defaultfg;
- extern unsigned int defaultbg;
- extern unsigned int defaultcs;
-+extern unsigned int scrollback_lines;
-diff --git a/x.c b/x.c
-index d73152b..75f3db1 100644
---- a/x.c
-+++ b/x.c
-_AT_@ -472,6 +472,23 @@ bpress(XEvent *e)
- 	struct timespec now;
- 	int snap;
- 
-+	if (btn == Button4 || btn == Button5) {
-+		Arg a;
-+		if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) {
-+			mousereport(e);
-+			return;
-+		}
-+		if (!tisaltscreen()) {
-+			a.i = 1;
-+			if (btn == Button4) {
-+				kscrollup(&a);
-+			} else {
-+				kscrolldown(&a);
-+			}
-+		}
-+		return;
-+	}
-+
- 	if (1 <= btn && btn <= 11)
- 		buttons |= 1 << (btn-1);
- 
--- 
-2.52.0
-
diff --git a/st.suckless.org/patches/scrollback-reflow-standalone/st-scrollback-reflow-standalone-0.9.31-extended.diff b/st.suckless.org/patches/scrollback-reflow-standalone/st-scrollback-reflow-standalone-extended-0.9.3.diff
similarity index 100%
rename from st.suckless.org/patches/scrollback-reflow-standalone/st-scrollback-reflow-standalone-0.9.31-extended.diff
rename to st.suckless.org/patches/scrollback-reflow-standalone/st-scrollback-reflow-standalone-extended-0.9.3.diff
Received on Fri Jan 16 2026 - 19:12:51 CET

This archive was generated by hypermail 2.3.0 : Fri Jan 16 2026 - 19:24:43 CET