diff --git a/xtrn/ddfilelister/ddfilelister.js b/xtrn/ddfilelister/ddfilelister.js
index fb7298bc62f8d3161640264e2012b6d54d7c66b7..c11bec4ed72c3084ab132e6f3291fcea016f30d0 100644
--- a/xtrn/ddfilelister/ddfilelister.js
+++ b/xtrn/ddfilelister/ddfilelister.js
@@ -156,6 +156,13 @@
  *                              Now optionally displays the number of files in the directory in the
  *                              header at the top of the list, configurable with the
  *                              displayNumFilesInHeader option in the config file
+ * 2025-02-22 Eric Oulashin     Version 2.28
+ *                              If extended descriptions are enabled and a filename is too long to
+ *                              fully fit in the menu, prepend the full filename (wrapped) to the
+ *                              description.
+ *                              New bottom line menu option to toggle extended descriptions on/off
+ *                              Fix: useFilenameIfNoDescription option now used in traditional
+ *                              (non-lightbar) mode.
  */
 
 "use strict";
@@ -197,8 +204,8 @@ var gAvatar = load({}, "avatar_lib.js");
 
 
 // Version information
-var LISTER_VERSION = "2.27";
-var LISTER_DATE = "2025-02-20";
+var LISTER_VERSION = "2.28";
+var LISTER_DATE = "2025-02-22";
 
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -285,8 +292,9 @@ var FILE_DOWNLOAD_SINGLE = 4;
 var FILE_EDIT = 5;
 var HELP = 6;
 var QUIT = 7;
-var FILE_MOVE = 8;   // Sysop action
-var FILE_DELETE = 9; // Sysop action
+var TOGGLE_EXTD_DESCS = 8; // Toggle extended descriptions
+var FILE_MOVE = 9;   // Sysop action
+var FILE_DELETE = 10; // Sysop action
 
 var NEXT_PAGE = 10;
 var PREV_PAGE = 11;
@@ -444,16 +452,16 @@ if (gFileList.length == 0)
 }
 
 // Construct and display the menu/command bar at the bottom of the screen
-var fileMenuBar = new DDFileMenuBar({ x: 1, y: console.screen_rows });
+var gFileMenuBar = new DDFileMenuBar({ x: 1, y: console.screen_rows });
 // Clear the screen and display the header lines
 console.clear("\x01n");
 if ((gListBehavior & FL_NO_HDR) != FL_NO_HDR)
 	displayFileLibAndDirHeader(false, null, !gUseLightbarInterface || !console.term_supports(USER_ANSI));
 // Create the file list menu (must be done after displayFileLibAndDirHeader() when using ANSI and lightbar)
-var gFileListMenu = createFileListMenu(fileMenuBar.getAllActionKeysStr(true, true) + KEY_LEFT + KEY_RIGHT + KEY_DEL /*+ CTRL_C*/);
+var gFileListMenu = createFileListMenu(gFileMenuBar.getAllActionKeysStr(true, true) + KEY_LEFT + KEY_RIGHT + KEY_DEL /*+ CTRL_C*/);
 if (gUseLightbarInterface && console.term_supports(USER_ANSI))
 {
-	fileMenuBar.writePromptLine();
+	gFileMenuBar.writePromptLine();
 	// In a loop, show the file list menu, allowing the user to scroll the file list,
 	// and respond to user input until the user decides to quit.
 	gFileListMenu.Draw({});
@@ -475,14 +483,25 @@ if (gUseLightbarInterface && console.term_supports(USER_ANSI))
 		if (lastUserInputUpper == null || lastUserInputUpper == "Q" || console.aborted)
 			continueDoingFileList = false;
 		else if (lastUserInputUpper == KEY_LEFT)
-			fileMenuBar.decrementMenuItemAndRefresh();
+			gFileMenuBar.decrementMenuItemAndRefresh();
 		else if (lastUserInputUpper == KEY_RIGHT)
-			fileMenuBar.incrementMenuItemAndRefresh();
+			gFileMenuBar.incrementMenuItemAndRefresh();
 		else if (lastUserInputUpper == KEY_ENTER)
 		{
-			currentActionVal = fileMenuBar.getCurrentSelectedAction();
-			fileMenuBar.setCurrentActionCode(currentActionVal);
-			actionRetObj = doAction(currentActionVal, gFileList, gFileListMenu);
+			currentActionVal = gFileMenuBar.getCurrentSelectedAction();
+			gFileMenuBar.setCurrentActionCode(currentActionVal);
+			if (currentActionVal == TOGGLE_EXTD_DESCS)
+			{
+				// Toggle extended descriptions
+				toggleExtdDescriptionsForUser_Lightbar();
+				drawFileListMenu = true; // Ensure the menu re-draws itself (properly)
+				actionRetObj = null;
+			}
+			else
+			{
+				// Handle actions (other than extended description toggle)
+				actionRetObj = doAction(currentActionVal, gFileList, gFileListMenu);
+			}
 		}
 		// Allow the delete key as a special key for sysops to delete the selected file(s). Also allow backspace
 		// due to some terminals returning backspace for delete.
@@ -490,16 +509,27 @@ if (gUseLightbarInterface && console.term_supports(USER_ANSI))
 		{
 			if (user.is_sysop)
 			{
-				fileMenuBar.setCurrentActionCode(FILE_DELETE, true);
+				gFileMenuBar.setCurrentActionCode(FILE_DELETE, true);
 				actionRetObj = doAction(FILE_DELETE, gFileList, gFileListMenu);
 				currentActionVal = FILE_DELETE;
 			}
 		}
 		else
 		{
-			currentActionVal = fileMenuBar.getActionFromChar(lastUserInputUpper, false);
-			fileMenuBar.setCurrentActionCode(currentActionVal, true);
-			actionRetObj = doAction(currentActionVal, gFileList, gFileListMenu);
+			currentActionVal = gFileMenuBar.getActionFromChar(lastUserInputUpper, false);
+			gFileMenuBar.setCurrentActionCode(currentActionVal, true);
+			if (currentActionVal == TOGGLE_EXTD_DESCS)
+			{
+				// Toggle extended descriptions
+				toggleExtdDescriptionsForUser_Lightbar();
+				drawFileListMenu = true; // Ensure the menu re-draws itself (properly)
+				actionRetObj = null;
+			}
+			else
+			{
+				// Handle actions (other than extended description toggle)
+				actionRetObj = doAction(currentActionVal, gFileList, gFileListMenu);
+			}
 		}
 		// If an action was done (actionRetObj is not null), then look at actionRetObj and
 		// do what's needed.  Note that quit (for the Q key) is already handled.
@@ -523,8 +553,8 @@ if (gUseLightbarInterface && console.term_supports(USER_ANSI))
 						displayFileLibAndDirHeader(false, null, gFileListMenu.numberedMode);
 					}
 				}
-				if (actionRetObj.reDrawCmdBar) // Could call fileMenuBar.constructPromptText(); if needed
-					fileMenuBar.writePromptLine();
+				if (actionRetObj.reDrawCmdBar) // Could call gFileMenuBar.constructPromptText(); if needed
+					gFileMenuBar.writePromptLine();
 				var redrewPartOfFileListMenu = false;
 				// If we are to re-draw the main screen content, then
 				// enable the flag to draw the file list menu on the next
@@ -643,7 +673,7 @@ else
 	var topItemIndexForLastPage = allFileInfoLines.length - numLinesPerPage;
 
 	// Allowed keys for user input
-	var validOptionKeys = "IVBDEMNPFLQ?" + KEY_PAGEDN + KEY_PAGEUP + KEY_HOME + KEY_END;
+	var validOptionKeys = "IVBDEMNPFLXQ?" + KEY_PAGEDN + KEY_PAGEUP + KEY_HOME + KEY_END;
 	if (user.is_sysop)
 		validOptionKeys += KEY_DEL + KEY_BACKSPACE;
 	// If the user's terminal supports ANSI, then also allow left, right, and enter (option navigation & selection)
@@ -680,16 +710,16 @@ else
 		}
 
 		if (refreshWholePromptLine || drawMenu)
-			fileMenuBar.pos = console.getxy();
+			gFileMenuBar.pos = console.getxy();
 		if (refreshWholePromptLine)
-			fileMenuBar.writePromptLine();
+			gFileMenuBar.writePromptLine();
 		var userInput = console.getkeys(validOptionKeys, -1, K_UPPER|K_NOECHO|K_NOSPIN|K_NOCRLF).toString();
 		// If the user pressed the enter key, change userInput to the key
 		// corresponding to what we'd expect for that option
 		if (userInput == KEY_ENTER)
 		{
-			//currentActionVal = fileMenuBar.getCurrentSelectedAction();
-			switch (fileMenuBar.getCurrentSelectedAction())
+			//currentActionVal = gFileMenuBar.getCurrentSelectedAction();
+			switch (gFileMenuBar.getCurrentSelectedAction())
 			{
 				case NEXT_PAGE:
 					userInput = KEY_PAGEDN;
@@ -711,14 +741,14 @@ else
 		// Check action based on the user's last input
 		if (userInput == KEY_LEFT)
 		{
-			fileMenuBar.decrementMenuItemAndRefresh();
+			gFileMenuBar.decrementMenuItemAndRefresh();
 			drawDirHeaderLines = false;
 			drawMenu = false;
 			refreshWholePromptLine = false;
 		}
 		else if (userInput == KEY_RIGHT)
 		{
-			fileMenuBar.incrementMenuItemAndRefresh();
+			gFileMenuBar.incrementMenuItemAndRefresh();
 			drawDirHeaderLines = false;
 			drawMenu = false;
 			refreshWholePromptLine = false;
@@ -728,8 +758,8 @@ else
 			drawDirHeaderLines = true;
 			drawMenu = true;
 			refreshWholePromptLine = true;
-			currentActionVal = fileMenuBar.getCurrentSelectedAction();
-			fileMenuBar.setCurrentActionCode(currentActionVal);
+			currentActionVal = gFileMenuBar.getCurrentSelectedAction();
+			gFileMenuBar.setCurrentActionCode(currentActionVal);
 			actionRetObj = doAction(currentActionVal, gFileList, gFileListMenu);
 		}
 		// Allow the delete key as a special key for sysops to delete the selected file(s). Also allow backspace
@@ -741,7 +771,7 @@ else
 				drawDirHeaderLines = true;
 				drawMenu = true;
 				refreshWholePromptLine = true;
-				fileMenuBar.setCurrentActionCode(FILE_DELETE, true);
+				gFileMenuBar.setCurrentActionCode(FILE_DELETE, true);
 				actionRetObj = doAction(FILE_DELETE, gFileList, gFileListMenu);
 				currentActionVal = FILE_DELETE;
 			}
@@ -807,8 +837,8 @@ else
 				refreshWholePromptLine = false;
 			}
 			currentActionVal = NEXT_PAGE;
-			//fileMenuBar.setCurrentActionCode(NEXT_PAGE, !refreshWholePromptLine);
-			fileMenuBar.setCurrentActionCode(NEXT_PAGE, true);
+			//gFileMenuBar.setCurrentActionCode(NEXT_PAGE, !refreshWholePromptLine);
+			gFileMenuBar.setCurrentActionCode(NEXT_PAGE, true);
 		}
 		else if (userInput == "P" || userInput == KEY_PAGEUP)
 		{
@@ -829,8 +859,8 @@ else
 				refreshWholePromptLine = false;
 			}
 			currentActionVal = PREV_PAGE;
-			//fileMenuBar.setCurrentActionCode(PREV_PAGE, !refreshWholePromptLine);
-			fileMenuBar.setCurrentActionCode(PREV_PAGE, true);
+			//gFileMenuBar.setCurrentActionCode(PREV_PAGE, !refreshWholePromptLine);
+			gFileMenuBar.setCurrentActionCode(PREV_PAGE, true);
 		}
 		else if (userInput == "F" || userInput == KEY_HOME)
 		{
@@ -849,8 +879,8 @@ else
 				refreshWholePromptLine = false;
 			}
 			currentActionVal = FIRST_PAGE;
-			//fileMenuBar.setCurrentActionCode(FIRST_PAGE, !refreshWholePromptLine);
-			fileMenuBar.setCurrentActionCode(FIRST_PAGE, true);
+			//gFileMenuBar.setCurrentActionCode(FIRST_PAGE, !refreshWholePromptLine);
+			gFileMenuBar.setCurrentActionCode(FIRST_PAGE, true);
 		}
 		else if (userInput == "L" || userInput == KEY_END)
 		{
@@ -869,16 +899,31 @@ else
 				refreshWholePromptLine = false;
 			}
 			currentActionVal = LAST_PAGE;
-			//fileMenuBar.setCurrentActionCode(LAST_PAGE, !refreshWholePromptLine);
-			fileMenuBar.setCurrentActionCode(LAST_PAGE, true);
+			//gFileMenuBar.setCurrentActionCode(LAST_PAGE, !refreshWholePromptLine);
+			gFileMenuBar.setCurrentActionCode(LAST_PAGE, true);
+		}
+		else if (userInput == "X")
+		{
+			// Toggle extended descriptions
+			var userCanToggle = (Boolean(user.settings & USER_EXTDESC) ? true : userCanEnableExtendedDescriptions());
+			if (userCanToggle)
+			{
+				user.settings ^= USER_EXTDESC;
+				allFileInfoLines = [];
+				for (var i = 0; i < gFileList.length; ++i)
+					allFileInfoLines = allFileInfoLines.concat(getFileInfoLineArrayForTraditionalUI(gFileList, i, formatInfo));
+				topItemIndexForLastPage = allFileInfoLines.length - numLinesPerPage;
+				drawMenu = true;
+				drawDirHeaderLines = true;
+			}
 		}
 		else
 		{
 			drawDirHeaderLines = true;
 			drawMenu = true;
 			refreshWholePromptLine = true;
-			currentActionVal = fileMenuBar.getActionFromChar(userInput, false);
-			fileMenuBar.setCurrentActionCode(currentActionVal, true);
+			currentActionVal = gFileMenuBar.getActionFromChar(userInput, false);
+			gFileMenuBar.setCurrentActionCode(currentActionVal, true);
 			actionRetObj = doAction(currentActionVal, gFileList, gFileListMenu);
 		}
 	}
@@ -992,6 +1037,29 @@ function doAction(pActionCode, pFileList, pFileListMenu)
 	return retObj;
 }
 
+// Toggles the user's extended descriptions. This is a special action case for the
+// lightbar interface which re-creates the lightbar menu, which will behave differently
+// depending on whether the user's extended descriptions are enabled or not.
+function toggleExtdDescriptionsForUser_Lightbar()
+{
+	// Toggle extended descriptions
+	var userCanToggle = (Boolean(user.settings & USER_EXTDESC) ? true : userCanEnableExtendedDescriptions());
+	if (userCanToggle)
+	{
+		var currentSelectedItemIdx = gFileListMenu.selectedItemIdx;
+		user.settings ^= USER_EXTDESC;
+		var listPopRetObj = populateFileList(gScriptMode);
+		if (listPopRetObj.exitNow) // Shouldn't happen here, but just in case
+			exit(0);
+		gFileListMenu = createFileListMenu(gFileMenuBar.getAllActionKeysStr(true, true) + KEY_LEFT + KEY_RIGHT + KEY_DEL /*+ CTRL_C*/);
+		gFileListMenu.SetSelectedItemIdx(currentSelectedItemIdx);
+		// If the user has enabled extended descriptions, then write the
+		// current selected file's extended description on the screen
+		if (Boolean(user.settings & USER_EXTDESC))
+			displayFileExtDescOnMainScreen(gFileListMenu.selectedItemIdx);
+	}
+}
+
 // Returns a string representing an action code, for relevant actions (not necessarily all actions)
 //
 // Parameters:
@@ -1949,6 +2017,8 @@ function displayHelpScreen()
 		printf(printfStr, "M", "Move the file(s) to another directory");
 		printf(printfStr, "DEL", "Delete the file(s)");
 	}
+	if (userCanEnableExtendedDescriptions())
+		printf(printfStr, "X", "Toggle extended descriptions (currently " + (Boolean(user.settings & USER_EXTDESC) ? "on" : "off") + ")");
 	printf(printfStr, "?", "Show this help screen");
 	// Ctrl-C for aborting (wanted to use isDoingFileSearch() but it seems even for searching/scanning,
 	// the mode is LIST_DIR rather than a search/scan
@@ -2627,7 +2697,6 @@ function DDFileMenuBar(pPos)
 		// The user is not a sysop; there is room for the Edit comand
 		this.cmdArray.push(new DDFileMenuBarItem("Edit", 0, FILE_EDIT));
 	}
-	//DDFileMenuBarItem(pItemText, pPos, pRetCode, pHotkeyOverride)
 	if (!gUseLightbarInterface || !console.term_supports(USER_ANSI))
 	{
 		this.cmdArray.push(new DDFileMenuBarItem("Next", 0, NEXT_PAGE, KEY_PAGEDN));
@@ -2635,6 +2704,16 @@ function DDFileMenuBar(pPos)
 		this.cmdArray.push(new DDFileMenuBarItem("First", 0, FIRST_PAGE));
 		this.cmdArray.push(new DDFileMenuBarItem("Last", 0, LAST_PAGE));
 	}
+	// If the user can enable extended descriptions (terminal size is big enough and the
+	// user's terminal supports ANSI), add a command ("Xtd") to toggle extended descriptions.
+	// And if the user's terminal is less than 87 characters wide and using the traditional
+	// (non-lightbar) user interface and the user is a sysop, don't make it visible because
+	// there wouldn't be enough room to display it.
+	if (userCanEnableExtendedDescriptions())
+	{
+		var displayXtdCmdText = !(console.screen_columns < 87 && !gUseLightbarInterface && user.is_sysop);
+		this.cmdArray.push(new DDFileMenuBarItem("Xtd", 0, TOGGLE_EXTD_DESCS, null, displayXtdCmdText));
+	}
 	this.cmdArray.push(new DDFileMenuBarItem("?", 0, HELP));
 	this.cmdArray.push(new DDFileMenuBarItem("Quit", 0, QUIT));
 
@@ -2648,12 +2727,19 @@ function DDFileMenuBar(pPos)
 // Return value: The number of additional solid blocks used to fill the whole screen row
 function DDFileMenuBar_constructPromptText()
 {
+	var numDisplayableItems = 0;
 	var totalItemTextLen = 0;
 	for (var i = 0; i < this.cmdArray.length; ++i)
-		totalItemTextLen += this.cmdArray[i].itemText.length;
+	{
+		if (this.cmdArray[i].displayItem)
+		{
+			++numDisplayableItems;
+			totalItemTextLen += this.cmdArray[i].itemText.length;
+		}
+	}
 	// The number of inner characters (without the outer solid blocks) is the total text
 	// length of all the items + 2 characters for each item except the last one
-	var numInnerChars = totalItemTextLen + (2 * (this.cmdArray.length-1));
+	var numInnerChars = totalItemTextLen + (2 * (numDisplayableItems-1));
 	// The number of solid blocks: Subtracting 11 because there will be 5 block characters on each side,
 	// and subtract 1 extra so it doesn't fill the last character on the screen
 	var numSolidBlocks = console.screen_columns - numInnerChars - 11;
@@ -2665,8 +2751,13 @@ function DDFileMenuBar_constructPromptText()
 	this.promptText += THIN_RECTANGLE_LEFT;
 	// Add the menu item text & block characters
 	var menuItemXPos = 6 + numSolidBlocksPerSide; // The X position of the start of item text for each item
+	var maxPromptLineLen = console.screen_columns - 1; // Maximum length of the prompt line
 	for (var i = 0; i < this.cmdArray.length; ++i)
 	{
+		// If the current item's displayItem property is false, then skip it.
+		if (!this.cmdArray[i].displayItem)
+			continue;
+
 		this.cmdArray[i].pos = menuItemXPos;
 		var numTrailingBlockChars = 0;
 		var selected = (i == this.currentCommandIdx);
@@ -2676,8 +2767,17 @@ function DDFileMenuBar_constructPromptText()
 			withTrailingBlock = true;
 			numTrailingBlockChars = 2;
 		}
-		menuItemXPos += this.cmdArray[i].itemText.length + numTrailingBlockChars;
-		this.promptText += this.getDDFileMenuBarItemText(this.cmdArray[i].itemText, selected, withTrailingBlock);
+		//menuItemXPos += this.cmdArray[i].itemText.length + numTrailingBlockChars;
+		//this.promptText += this.getDDFileMenuBarItemText(this.cmdArray[i].itemText, selected, withTrailingBlock);
+
+		// If the line with this item text would be short enough to fit on the screen,
+		// then add this item's text
+		var numCharsNeeded = this.cmdArray[i].itemText.length + numTrailingBlockChars;
+		if (console.strlen(this.promptText) + numCharsNeeded + numSolidBlocksPerSide <= maxPromptLineLen)
+		{
+			menuItemXPos += this.cmdArray[i].itemText.length + numTrailingBlockChars;
+			this.promptText += this.getDDFileMenuBarItemText(this.cmdArray[i].itemText, selected, withTrailingBlock);
+		}
 	}
 	// Add the right-side blocks
 	this.promptText += "\x01w" + THIN_RECTANGLE_RIGHT;
@@ -2708,7 +2808,9 @@ function DDFileMenuBar_refreshWithNewAction(pCmdIdx)
 {
 	if (typeof(pCmdIdx) !== "number")
 		return;
-	if (pCmdIdx == this.currentCommandIdx)
+	if (pCmdIdx == this.currentCommandIdx || pCmdIdx < 0 || pCmdIdx >= this.cmdArray.length)
+		return;
+	if (!this.cmdArray[pCmdIdx].displayItem)
 		return;
 
 	// Refresh the prompt area for the previous index with regular colors
@@ -2767,8 +2869,14 @@ function DDFileMenuBar_getDDFileMenuBarItemText(pText, pSelected, pWithTrailingB
 function DDFileMenuBar_incrementMenuItemAndRefresh()
 {
 	var newCmdIdx = this.currentCommandIdx + 1;
+	while (newCmdIdx < this.cmdArray.length && !this.cmdArray[newCmdIdx].displayItem)
+		++newCmdIdx;
 	if (newCmdIdx >= this.cmdArray.length)
+	{
 		newCmdIdx = 0;
+		while (newCmdIdx < this.currentCommandIdx && !this.cmdArray[newCmdIdx].displayItem)
+			++newCmdIdx;
+	}
 	// Will set this.currentCommandIdx
 	this.refreshWithNewAction(newCmdIdx);
 }
@@ -2777,8 +2885,14 @@ function DDFileMenuBar_incrementMenuItemAndRefresh()
 function DDFileMenuBar_decrementMenuItemAndRefresh()
 {
 	var newCmdIdx = this.currentCommandIdx - 1;
+	while (newCmdIdx > 0 && !this.cmdArray[newCmdIdx].displayItem)
+		--newCmdIdx;
 	if (newCmdIdx < 0)
+	{
 		newCmdIdx = this.cmdArray.length - 1;
+		while (newCmdIdx > this.currentCommandIdx && !this.cmdArray[newCmdIdx].displayItem)
+			--newCmdIdx;
+	}
 	// Will set this.currentCommandIdx
 	this.refreshWithNewAction(newCmdIdx);
 }
@@ -2850,7 +2964,7 @@ function DDFileMenuBar_setCurrentActionCode(pActionCode, pRefreshOnScreen)
 	{
 		if (this.cmdArray[i].retCode == pActionCode)
 		{
-			if (refreshOnScreen)
+			if (refreshOnScreen && this.cmdArray[i].displayItem)
 				this.refreshWithNewAction(i);
 			else
 			{
@@ -2890,8 +3004,9 @@ function DDFileMenuBar_getAllActionKeysStr(pLowercase, pUppercase)
 //  pItemText: The text of the item
 //  pPos: Horizontal (or vertical) starting location in the bar
 //  pRetCode: The item's return code
-//  pHotkeyOverride: Optional: A key to use for the action instead of the first character in pItemText
-function DDFileMenuBarItem(pItemText, pPos, pRetCode, pHotkeyOverride)
+//  pHotkeyOverride: Optional - A key to use for the action instead of the first character in pItemText
+//  pDisplayItem: Optional - Whether or not this item should be displayed on the menu bar. Defaults to true.
+function DDFileMenuBarItem(pItemText, pPos, pRetCode, pHotkeyOverride, pDisplayItem)
 {
 	this.itemText = pItemText;
 	this.pos = pPos;
@@ -2899,6 +3014,7 @@ function DDFileMenuBarItem(pItemText, pPos, pRetCode, pHotkeyOverride)
 	this.hotkeyOverride = null;
 	if (pHotkeyOverride != null && typeof(pHotkeyOverride) !== "undefined")
 		this.hotkeyOverride = pHotkeyOverride;
+	this.displayItem = (typeof(pDisplayItem) === "boolean" ? pDisplayItem : true);
 }
 
 
@@ -3326,7 +3442,6 @@ function displayListHdrLine(pMoveToLocationFirst, pNumberedMode)
 // Return value: The DDLightbarMenu object for the file list in the file directory
 function createFileListMenu(pQuitKeys)
 {
-	//DDLightbarMenu(pX, pY, pWidth, pHeight)
 	// Create the menu object.  Place it below the header lines (which should have been written
 	// before this), and also leave 1 row at the bottom for the prompt line
 	var startRow = gNumHeaderLinesDisplayed > 0 ? gNumHeaderLinesDisplayed + 1 : 1;
@@ -4577,7 +4692,7 @@ function populateFileList(pSearchMode)
 		exitNow: false,
 		exitCode: 0
 	};
-	
+
 	var dirErrors = [];
 	var allSameDir = true;
 
@@ -5160,6 +5275,13 @@ function extendedDescEnabled()
 	return userExtDescEnabled && console.screen_columns >= 80 && gUseLightbarInterface && console.term_supports(USER_ANSI);
 }
 
+// Returns whether the user can enable extended descriptions (depends on their terminal size and whether
+// their terminal can use ANSI)
+function userCanEnableExtendedDescriptions()
+{
+	return console.screen_columns >= 80 && console.term_supports(USER_ANSI);
+}
+
 // Displays a file's extended description on the main screen, next to the
 // file list menu.  This is to be used when the user's extended file description
 // option is enabled (where the menu would take up about the left half of
@@ -5230,10 +5352,15 @@ function displayFileExtDescOnMainScreen(pFileIdx, pStartScreenRow, pEndScreenRow
 	fileDesc = removeOrReplaceSyncCursorMovementChars(fileDesc);
 	// If there is no description and the option to use the filename is enabled, then use the
 	// filename.
-	if (gUseFilenameIfNoDescription && (fileDesc == "" || /^\s+$/.test(fileDesc)))
+	var fileDescIsEmptyOrWhitespace = (fileDesc == "" || /^\s+$/.test(fileDesc));
+	if (gUseFilenameIfNoDescription && fileDescIsEmptyOrWhitespace)
 		fileDesc = lfexpand(word_wrap(fileMetadata.name + "\r\n(No description)", maxDescLen, null, false));
+	// If there is a description and the filename is too long to fit on the menu, then prepend the
+	// full filename (wrapped) to the the description
+	else if (!fileDescIsEmptyOrWhitespace && fileMetadata.name.length > gFileListMenu.filenameLen)
+		fileDesc = lfexpand(word_wrap(fileMetadata.name, maxDescLen, null, false)) + "\r\n" + fileDesc;
+	// Display the description on the screen
 	var fileDescArray = fileDesc.split("\r\n");
-	//if (user.is_sysop) console.print("\x01nfileDescArray is array: " + Array.isArray(fileDescArray) + "  \x01p"); // Temporary
 	console.attributes = "N";
 	// screenRowNum is to keep track of the row on the screen where the
 	// description line would be placed, in case the start row is after that
@@ -5498,9 +5625,8 @@ function getFileInfoLineArrayForTraditionalUI(pFileList, pIdx, pFormatInfo)
 	if (pFileList[pIdx] == undefined)
 		return [];
 
-	var userExtDescEnabled = ((user.settings & USER_EXTDESC) == USER_EXTDESC);
 	var descLines;
-	if (userExtDescEnabled)
+	if (Boolean(user.settings & USER_EXTDESC)) // If extended descriptions
 		descLines = getExtdFileDescArray(pFileList, pIdx);
 	else
 	{
@@ -5510,14 +5636,38 @@ function getFileInfoLineArrayForTraditionalUI(pFileList, pIdx, pFormatInfo)
 	if (!Array.isArray(descLines))
 		descLines = [];
 	if (descLines.length == 0)
-		descLines.push("");
+	{
+		// There is no description. If the option to use the filename is enabled, then use the filename.
+		if (gUseFilenameIfNoDescription)
+		{
+			var fileDesc = lfexpand(word_wrap(pFileList[pIdx].name + "\r\n(No description)", pFormatInfo.descLen, null, false));
+			var fileDescArray = fileDesc.split("\r\n");
+			for (var i = 0; i < fileDescArray.length; ++i)
+				descLines.push(fileDescArray[i]);
+		}
+		else
+			descLines.push("");
+	}
+	else
+	{
+		// If the filename is too long to fit on the menu, then prepend the full filename (wrapped) to
+		// the the description.
+		if (pFileList[pIdx].name.length > gFileListMenu.filenameLen)
+		{
+			var filenameLines = [];
+			var fileDescArray = lfexpand(word_wrap(pFileList[pIdx].name, pFormatInfo.descLen, null, false)).split("\r\n");
+			for (var i = 0; i < fileDescArray.length; ++i)
+				filenameLines.push(fileDescArray[i]);
+			descLines = filenameLines.concat(descLines);
+		}
+	}
 
 	var filename = shortenFilename(pFileList[pIdx].name, pFormatInfo.filenameLen, true);
 	// Note: substrWithAttrCodes() is defined in dd_lightbar_menu.js
 	var fileInfoLines = [];
 	var fileSizeStr = file_size_str(pFileList[pIdx].size, null, FILE_SIZE_PRECISION);
 	fileInfoLines.push(format(pFormatInfo.formatStr, pIdx+1, filename, fileSizeStr.substr(0, pFormatInfo.fileSizeLen), substrWithAttrCodes(descLines[0], 0, pFormatInfo.descLen)));
-	if (userExtDescEnabled)
+	if (Boolean(user.settings & USER_EXTDESC)) // If extended descriptions
 	{
 		for (var i = 1; i < descLines.length; ++i)
 			fileInfoLines.push(format(pFormatInfo.formatStrExtdDescLines, substrWithAttrCodes(descLines[i], 0, pFormatInfo.descLen)));
diff --git a/xtrn/ddfilelister/readme.txt b/xtrn/ddfilelister/readme.txt
index 8ceb186fc070540f43aa1420b64a44a9fa4f295e..56bc50dbe15406671f2ec40db952611a2c40eb18 100644
--- a/xtrn/ddfilelister/readme.txt
+++ b/xtrn/ddfilelister/readme.txt
@@ -1,6 +1,6 @@
                         Digital Distortion File Lister
-                                 Version 2.27
-                           Release date: 2025-02-20
+                                 Version 2.28
+                           Release date: 2025-02-22
 
                                      by
 
@@ -35,10 +35,10 @@ that there are no serious issues with it (at least, none that I have seen).
 
 2. Introduction
 ===============
-This release is version 2.00 because I had previously released a message lister
-mod for Synchronet which was just a list header and a command bar to display
-under the list, and it still used Synchronet's stock file list.  Now that
-Synchronet provides a JavaScript interface to its filebases (as of version
+This release is version 2.## because I had previously released a message lister
+mod (version 1.##) for Synchronet which was just a list header and a command bar
+to display under the list, and it still used Synchronet's stock file list.  Now
+that Synchronet provides a JavaScript interface to its filebases (as of version
 3.19), more customization is possible with JavaScript.
 
 Digital Distortion File Lister is a script for Synchronet that provides an
@@ -59,6 +59,21 @@ side.  If the user's extended file description setting is disabled, the
 lightbar file menu will use the entire width of the screen, with the short
 file descriptions being displayed in a single row with each file.
 
+In order to display extended descriptions, however, this file lister currently
+requires ANSI support (for cursor movements & such) and a terminal width of at
+least 80 characters in order to display everything, since the extended
+descriptions will be displayed to the right of the menu. If the user's terminal
+doesn't meet these requirements, short descriptions will be displayed.
+
+The user can toggle extended descriptions on/off from within this file lister
+using the X key. However, if the user's terminal doesn't meet the above
+requiremtnts for extended description mode, the user won't be able to toggle
+extended descriptions from within this file lister.
+
+If a filename is too long to be fully displayed in the menu item, the full file
+description will be displayed above the description (wrapped to the description
+area width) if extended descriptions are enabled.
+
 When adding files to the user's batch download queue or (for the sysop)
 selecting files to move or delete, multi-select mode can be used, allowing
 the user to select multiple files using the spacebar.  If the spacebar is not
diff --git a/xtrn/ddfilelister/revision_history.txt b/xtrn/ddfilelister/revision_history.txt
index 36836b7a443ca153caabd9bcfc01ff89534b4004..40a507001d932fd06915a178ed49cad518678322 100644
--- a/xtrn/ddfilelister/revision_history.txt
+++ b/xtrn/ddfilelister/revision_history.txt
@@ -5,6 +5,13 @@ Revision History (change log)
 =============================
 Version  Date         Description
 -------  ----         -----------
+2.28     2025-02-22   If extended descriptions are enabled and a filename is
+                      too long to fully fit in the menu, prepend the full
+                      filename (wrapped) to the description.
+                      New bottom line menu option to toggle extended
+                      descriptions on/off
+                      Fix: useFilenameIfNoDescription option now used in
+                      traditional (non-lightbar) mode.
 2.27     2025-02-20   Now optionally displays the number of files in the
                       directory in the header at the top of the list,
                       configurable with the displayNumFilesInHeader option in