Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8f4bf0bd90 | ||
|
|
b63e038a40 |
+140
-53
@@ -1,29 +1,151 @@
|
||||
Console.Write("Block Name(diamon_ore): ");
|
||||
string blockname = Console.ReadLine();
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
|
||||
if (string.IsNullOrEmpty(blockname))
|
||||
|
||||
namespace txtgen;
|
||||
|
||||
public class Program
|
||||
{
|
||||
|
||||
|
||||
// string blockname;
|
||||
static string ctm_dir;
|
||||
static void Main()
|
||||
{
|
||||
|
||||
Console.OutputEncoding = System.Text.Encoding.UTF8;
|
||||
Console.CursorVisible = true;
|
||||
|
||||
// Dedupe, sort alphabetically once for consistent display
|
||||
var blocks = AutoComplete.FullBlocks.Distinct().OrderBy(b => b).ToList();
|
||||
|
||||
Console.WriteLine("Minecraft 26.2 (Chaos Cubed) — Full Block Picker");
|
||||
// Console.WriteLine("Type to filter, Up/Down to move, Tab/Enter to accept, Esc to cancel.");
|
||||
Console.WriteLine();
|
||||
|
||||
string chosen = AutoComplete.AutoCompletePrompt("Block Name: ", blocks);
|
||||
|
||||
Console.WriteLine();
|
||||
if (chosen == null)
|
||||
return;
|
||||
// else
|
||||
// Console.WriteLine($"You selected: {chosen}");
|
||||
|
||||
|
||||
|
||||
// Console.Write("Block Name(diamon_ore): ");
|
||||
string blockname = chosen;
|
||||
|
||||
if (string.IsNullOrEmpty(blockname))
|
||||
{
|
||||
blockname = "diamond_ore";
|
||||
}
|
||||
}
|
||||
|
||||
Console.Write("top texture url: ");
|
||||
string top_texture = Console.ReadLine().Trim('\"');
|
||||
if (Directory.Exists("output/assets/minecraft/optifine/ctm/" + blockname))
|
||||
{
|
||||
Console.WriteLine("this block already exists in the texturepack delete it and try again");
|
||||
return;
|
||||
}
|
||||
Create_pack(blockname);
|
||||
|
||||
Console.Write("bottom texture url: ");
|
||||
string bottom_texture = Console.ReadLine().Trim('\"');
|
||||
// Console.Write("top texture url: ");
|
||||
// string top_texture = Console.ReadLine().Trim('\"');
|
||||
|
||||
Console.Write("both texture url: ");
|
||||
string both_texture = Console.ReadLine().Trim('\"');
|
||||
// Console.Write("bottom texture url: ");
|
||||
// string bottom_texture = Console.ReadLine().Trim('\"');
|
||||
|
||||
Console.Write("none texture url: ");
|
||||
string none_texture = Console.ReadLine().Trim('\"');
|
||||
Console.Write("full texture path: ");
|
||||
string full_texture = Console.ReadLine().Trim('\"');
|
||||
|
||||
Console.Write("none texture path: ");
|
||||
string none_texture = Console.ReadLine().Trim('\"');
|
||||
|
||||
|
||||
string working_dir = Directory.GetCurrentDirectory();
|
||||
// string blockname = "blockname";
|
||||
Directory.CreateDirectory(working_dir + "/output/assets");
|
||||
Image full_img = Image.Load(full_texture);
|
||||
Image none_img = Image.Load(none_texture);
|
||||
|
||||
string mcmeta = @"{
|
||||
Image bottom_img = Image.Load(full_texture);
|
||||
Image top_img = Image.Load(none_texture);
|
||||
int size = full_img.Height;
|
||||
|
||||
|
||||
bottom_img.Mutate(x => x.DrawImage(none_img, new Rectangle(0, 0, size, size / 2), 1f));
|
||||
top_img.Mutate(x => x.DrawImage(full_img, new Rectangle(0, 0, size, size / 2), 1f));
|
||||
|
||||
|
||||
// g.Save();
|
||||
if (Directory.Exists("tmp"))
|
||||
{
|
||||
Directory.Delete("tmp", true);
|
||||
}
|
||||
Directory.CreateDirectory("tmp");
|
||||
|
||||
bottom_img.Save("tmp/bottom.png");
|
||||
top_img.Save("tmp/top.png");
|
||||
|
||||
string top_texture = "tmp/top.png";
|
||||
string bottom_texture = "tmp/bottom.png";
|
||||
|
||||
Add_block(blockname, top_texture, bottom_texture, full_texture, none_texture);
|
||||
|
||||
Directory.Delete("tmp", true);
|
||||
// string blockname = "blockname";
|
||||
|
||||
|
||||
// Directory.CreateDirectory(working_dir + "/output/assets/minecraft");
|
||||
// Directory.CreateDirectory(working_dir + "/output/assets/minecraft/optifine");
|
||||
// Directory.CreateDirectory(working_dir + "/output/assets/minecraft/optifine/ctm");
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
static void Add_block(string blockname, string top_texture, string bottom_texture, string full_texture, string none_texture)
|
||||
{
|
||||
string ctmproperties = @"matchTiles=" + blockname + @"
|
||||
method=ctm
|
||||
tiles=0-47";
|
||||
|
||||
File.WriteAllText(ctm_dir + "/" + blockname + ".properties", ctmproperties);
|
||||
|
||||
int[] top = { 4, 5, 7, 12, 13, 14, 15, 29, 31 };
|
||||
int[] bottom = { 16, 17, 18, 36, 37, 38, 39, 40, 42 };
|
||||
int[] both = { 0, 1, 2, 3 };
|
||||
int[] none = { 6, 8, 9, 10, 11, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 30, 32, 33, 34, 35, 41, 43, 44, 45, 46 };
|
||||
|
||||
|
||||
|
||||
|
||||
foreach (int i in top)
|
||||
{
|
||||
File.Copy(top_texture, ctm_dir + "/" + i + ".png");
|
||||
}
|
||||
foreach (int i in bottom)
|
||||
{
|
||||
File.Copy(bottom_texture, ctm_dir + "/" + i + ".png");
|
||||
}
|
||||
foreach (int i in both)
|
||||
{
|
||||
File.Copy(full_texture, ctm_dir + "/" + i + ".png");
|
||||
}
|
||||
foreach (int i in none)
|
||||
{
|
||||
File.Copy(none_texture, ctm_dir + "/" + i + ".png");
|
||||
}
|
||||
}
|
||||
static void Create_pack(string blockname)
|
||||
{
|
||||
string working_dir = Directory.GetCurrentDirectory();
|
||||
|
||||
string pack_Name = "output";
|
||||
|
||||
ctm_dir = Path.Combine(working_dir,pack_Name, "assets/minecraft/optifine/ctm/" + blockname);
|
||||
|
||||
Directory.CreateDirectory(ctm_dir);
|
||||
// Directory.CreateDirectory(working_dir + "/1output/assets");
|
||||
|
||||
string mcmeta = @"{
|
||||
""pack"": {
|
||||
""description"": """",
|
||||
""pack_format"": 15,
|
||||
@@ -33,42 +155,7 @@ string mcmeta = @"{
|
||||
}
|
||||
}";
|
||||
|
||||
File.WriteAllText(working_dir + "/output/pack.mcmeta", mcmeta);
|
||||
File.WriteAllText(Path.Combine(working_dir, pack_Name, "pack.mcmeta"), mcmeta);
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(working_dir + "/output/assets/minecraft");
|
||||
Directory.CreateDirectory(working_dir + "/output/assets/minecraft/optifine");
|
||||
Directory.CreateDirectory(working_dir + "/output/assets/minecraft/optifine/ctm");
|
||||
Directory.CreateDirectory(working_dir + "/output/assets/minecraft/optifine/ctm/" + blockname);
|
||||
|
||||
|
||||
string ctm_dir = working_dir + "/output/assets/minecraft/optifine/ctm/" + blockname;
|
||||
string ctmproperties = @"matchTiles=" + blockname + @"
|
||||
method=ctm
|
||||
tiles=0-47";
|
||||
|
||||
File.WriteAllText(ctm_dir + "/" + blockname + ".properties", ctmproperties);
|
||||
|
||||
int[] top = {4,5,7,12,13,14,15,29,31};
|
||||
int[] bottom = {16,17,18,36,37,38,39,40,42};
|
||||
int[] both = {0,1,2,3};
|
||||
int[] none = {6,8,9,10,11,19,20,21,22,23,24,25,26,27,28,30,32,33,34,35,41,43,44,45,46};
|
||||
|
||||
|
||||
|
||||
|
||||
foreach (int i in top)
|
||||
{
|
||||
File.Copy(top_texture, ctm_dir + "/" + i + ".png");
|
||||
}
|
||||
foreach (int i in bottom)
|
||||
{
|
||||
File.Copy(bottom_texture, ctm_dir + "/" + i + ".png");
|
||||
}
|
||||
foreach (int i in both)
|
||||
{
|
||||
File.Copy(both_texture, ctm_dir + "/" + i + ".png");
|
||||
}
|
||||
foreach (int i in none)
|
||||
{
|
||||
File.Copy(none_texture, ctm_dir + "/" + i + ".png");
|
||||
}
|
||||
+344
@@ -0,0 +1,344 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace txtgen
|
||||
{
|
||||
public struct AutoComplete
|
||||
{
|
||||
/// <summary>
|
||||
/// This is mostly ai generated
|
||||
/// </summary>
|
||||
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Minecraft 26.2 "Chaos Cubed" — FULL BLOCKS ONLY
|
||||
// (Solid 1x1x1 cube blocks. No slabs, stairs, walls, fences, doors,
|
||||
// carpets, plants, signs, or other non-full-cube shapes.)
|
||||
// ----------------------------------------------------------------
|
||||
public static readonly List<string> FullBlocks = new List<string>
|
||||
{
|
||||
// --- Stone & natural ---
|
||||
"stone", "granite", "polished_granite", "diorite", "polished_diorite",
|
||||
"andesite", "polished_andesite", "deepslate", "cobbled_deepslate",
|
||||
"polished_deepslate", "calcite", "tuff", "polished_tuff", "tuff_bricks",
|
||||
"chiseled_tuff", "chiseled_tuff_bricks", "dripstone_block",
|
||||
"cobblestone", "mossy_cobblestone", "smooth_stone", "stone_bricks",
|
||||
"mossy_stone_bricks", "cracked_stone_bricks", "chiseled_stone_bricks",
|
||||
"bedrock", "dirt", "coarse_dirt", "rooted_dirt", "podzol", "mud",
|
||||
"packed_mud", "mud_bricks", "grass_block", "mycelium", "sand",
|
||||
"red_sand", "sandstone", "chiseled_sandstone", "cut_sandstone",
|
||||
"smooth_sandstone", "red_sandstone", "chiseled_red_sandstone",
|
||||
"cut_red_sandstone", "smooth_red_sandstone", "gravel", "clay",
|
||||
"terracotta", "obsidian", "crying_obsidian", "ice", "packed_ice",
|
||||
"blue_ice", "snow_block", "powder_snow", "moss_block", "mangrove_roots",
|
||||
"muddy_mangrove_roots", "sponge", "wet_sponge",
|
||||
|
||||
// --- Sulfur caves (new in 26.2 "Chaos Cubed") ---
|
||||
"sulfur", "polished_sulfur", "sulfur_bricks", "chiseled_sulfur",
|
||||
"cinnabar", "polished_cinnabar", "cinnabar_bricks", "chiseled_cinnabar",
|
||||
|
||||
// --- Ores ---
|
||||
"coal_ore", "deepslate_coal_ore", "iron_ore", "deepslate_iron_ore",
|
||||
"copper_ore", "deepslate_copper_ore", "gold_ore", "deepslate_gold_ore",
|
||||
"redstone_ore", "deepslate_redstone_ore", "emerald_ore",
|
||||
"deepslate_emerald_ore", "lapis_ore", "deepslate_lapis_ore",
|
||||
"diamond_ore", "deepslate_diamond_ore", "nether_gold_ore",
|
||||
"nether_quartz_ore", "ancient_debris",
|
||||
|
||||
// --- Mineral blocks ---
|
||||
"coal_block", "iron_block", "copper_block", "exposed_copper",
|
||||
"weathered_copper", "oxidized_copper", "waxed_copper_block",
|
||||
"waxed_exposed_copper", "waxed_weathered_copper", "waxed_oxidized_copper",
|
||||
"cut_copper", "exposed_cut_copper", "weathered_cut_copper",
|
||||
"oxidized_cut_copper", "waxed_cut_copper", "chiseled_copper",
|
||||
"exposed_chiseled_copper", "weathered_chiseled_copper",
|
||||
"oxidized_chiseled_copper", "gold_block", "redstone_block",
|
||||
"emerald_block", "lapis_block", "diamond_block", "netherite_block",
|
||||
"raw_iron_block", "raw_copper_block", "raw_gold_block",
|
||||
|
||||
// --- Logs, wood & planks ---
|
||||
"oak_log", "spruce_log", "birch_log", "jungle_log", "acacia_log",
|
||||
"dark_oak_log", "mangrove_log", "cherry_log", "pale_oak_log",
|
||||
"crimson_stem", "warped_stem",
|
||||
"stripped_oak_log", "stripped_spruce_log", "stripped_birch_log",
|
||||
"stripped_jungle_log", "stripped_acacia_log", "stripped_dark_oak_log",
|
||||
"stripped_mangrove_log", "stripped_cherry_log", "stripped_pale_oak_log",
|
||||
"stripped_crimson_stem", "stripped_warped_stem",
|
||||
"oak_wood", "spruce_wood", "birch_wood", "jungle_wood", "acacia_wood",
|
||||
"dark_oak_wood", "mangrove_wood", "cherry_wood", "pale_oak_wood",
|
||||
"crimson_hyphae", "warped_hyphae",
|
||||
"stripped_oak_wood", "stripped_spruce_wood", "stripped_birch_wood",
|
||||
"stripped_jungle_wood", "stripped_acacia_wood", "stripped_dark_oak_wood",
|
||||
"stripped_mangrove_wood", "stripped_cherry_wood", "stripped_pale_oak_wood",
|
||||
"stripped_crimson_hyphae", "stripped_warped_hyphae",
|
||||
"oak_planks", "spruce_planks", "birch_planks", "jungle_planks",
|
||||
"acacia_planks", "dark_oak_planks", "mangrove_planks", "cherry_planks",
|
||||
"pale_oak_planks", "bamboo_planks", "crimson_planks", "warped_planks",
|
||||
"bamboo_block", "stripped_bamboo_block", "mangrove_mangrove_roots",
|
||||
|
||||
// --- Leaves ---
|
||||
"oak_leaves", "spruce_leaves", "birch_leaves", "jungle_leaves",
|
||||
"acacia_leaves", "dark_oak_leaves", "mangrove_leaves", "cherry_leaves",
|
||||
"pale_oak_leaves", "azalea_leaves", "flowering_azalea_leaves",
|
||||
|
||||
// --- Wool, concrete, terracotta, glass, glazed terracotta (16 colors) ---
|
||||
"white_wool","orange_wool","magenta_wool","light_blue_wool","yellow_wool",
|
||||
"lime_wool","pink_wool","gray_wool","light_gray_wool","cyan_wool",
|
||||
"purple_wool","blue_wool","brown_wool","green_wool","red_wool","black_wool",
|
||||
|
||||
"white_concrete","orange_concrete","magenta_concrete","light_blue_concrete",
|
||||
"yellow_concrete","lime_concrete","pink_concrete","gray_concrete",
|
||||
"light_gray_concrete","cyan_concrete","purple_concrete","blue_concrete",
|
||||
"brown_concrete","green_concrete","red_concrete","black_concrete",
|
||||
|
||||
"white_concrete_powder","orange_concrete_powder","magenta_concrete_powder",
|
||||
"light_blue_concrete_powder","yellow_concrete_powder","lime_concrete_powder",
|
||||
"pink_concrete_powder","gray_concrete_powder","light_gray_concrete_powder",
|
||||
"cyan_concrete_powder","purple_concrete_powder","blue_concrete_powder",
|
||||
"brown_concrete_powder","green_concrete_powder","red_concrete_powder",
|
||||
"black_concrete_powder",
|
||||
|
||||
"white_terracotta","orange_terracotta","magenta_terracotta",
|
||||
"light_blue_terracotta","yellow_terracotta","lime_terracotta",
|
||||
"pink_terracotta","gray_terracotta","light_gray_terracotta",
|
||||
"cyan_terracotta","purple_terracotta","blue_terracotta",
|
||||
"brown_terracotta","green_terracotta","red_terracotta","black_terracotta",
|
||||
|
||||
"white_glazed_terracotta","orange_glazed_terracotta","magenta_glazed_terracotta",
|
||||
"light_blue_glazed_terracotta","yellow_glazed_terracotta","lime_glazed_terracotta",
|
||||
"pink_glazed_terracotta","gray_glazed_terracotta","light_gray_glazed_terracotta",
|
||||
"cyan_glazed_terracotta","purple_glazed_terracotta","blue_glazed_terracotta",
|
||||
"brown_glazed_terracotta","green_glazed_terracotta","red_glazed_terracotta",
|
||||
"black_glazed_terracotta",
|
||||
|
||||
"glass","white_stained_glass","orange_stained_glass","magenta_stained_glass",
|
||||
"light_blue_stained_glass","yellow_stained_glass","lime_stained_glass",
|
||||
"pink_stained_glass","gray_stained_glass","light_gray_stained_glass",
|
||||
"cyan_stained_glass","purple_stained_glass","blue_stained_glass",
|
||||
"brown_stained_glass","green_stained_glass","red_stained_glass",
|
||||
"black_stained_glass","tinted_glass",
|
||||
|
||||
// --- Prismarine ---
|
||||
"prismarine", "prismarine_bricks", "dark_prismarine", "sea_lantern",
|
||||
|
||||
// --- Quartz & purpur ---
|
||||
"quartz_block", "chiseled_quartz_block", "smooth_quartz",
|
||||
"quartz_bricks", "quartz_pillar",
|
||||
"purpur_block", "purpur_pillar",
|
||||
|
||||
// --- Nether ---
|
||||
"netherrack", "soul_sand", "soul_soil", "basalt", "polished_basalt",
|
||||
"smooth_basalt", "blackstone", "polished_blackstone",
|
||||
"chiseled_polished_blackstone", "polished_blackstone_bricks",
|
||||
"cracked_polished_blackstone_bricks", "gilded_blackstone",
|
||||
"nether_bricks", "red_nether_bricks", "cracked_nether_bricks",
|
||||
"chiseled_nether_bricks", "nether_wart_block", "warped_wart_block",
|
||||
"shroomlight", "glowstone", "magma_block", "crimson_nylium",
|
||||
"warped_nylium",
|
||||
|
||||
// --- End ---
|
||||
"end_stone", "end_stone_bricks", "purpur_block_end", "bone_block",
|
||||
|
||||
// --- Utility & misc full blocks ---
|
||||
"crafting_table", "furnace", "blast_furnace", "smoker", "cartography_table",
|
||||
"fletching_table", "smithing_table", "loom", "composter_block",
|
||||
"barrel", "chest", "trapped_chest", "ender_chest", "shulker_box",
|
||||
"tnt", "note_block", "jukebox", "bookshelf", "chiseled_bookshelf",
|
||||
"lodestone", "respawn_anchor", "beacon", "conduit", "dragon_egg",
|
||||
"spawner", "hay_block", "target", "honeycomb_block", "honey_block",
|
||||
"slime_block", "dried_kelp_block", "melon", "pumpkin",
|
||||
"carved_pumpkin", "jack_o_lantern", "sculk", "sculk_catalyst",
|
||||
"amethyst_block", "budding_amethyst", "iron_block_full",
|
||||
"waxed_block_of_copper", "mushroom_stem", "red_mushroom_block",
|
||||
"brown_mushroom_block", "resin_block", "chiseled_resin_bricks",
|
||||
"resin_bricks", "ochre_froglight", "verdant_froglight",
|
||||
"pearlescent_froglight", "reinforced_deepslate", "suspicious_sand",
|
||||
"suspicious_gravel", "command_block", "chain_command_block",
|
||||
"repeating_command_block", "structure_block", "jigsaw_block",
|
||||
"barrier", "light_block", "moving_piston",
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Renders a LazyVim-style inline autocomplete popup below the input line.
|
||||
/// Returns the accepted string, or null if the user pressed Esc.
|
||||
/// </summary>
|
||||
public static string AutoCompletePrompt(string prompt, List<string> options)
|
||||
{
|
||||
const int maxVisible = 8; // rows shown in the dropdown
|
||||
|
||||
string buffer = "";
|
||||
int selectedIndex = 0; // index within the filtered list
|
||||
int scrollOffset = 0; // first visible row when list is longer than maxVisible
|
||||
|
||||
int inputTop = Console.CursorTop;
|
||||
int inputLeft = prompt.Length;
|
||||
|
||||
List<string> filtered = FilterAndRank(options, buffer);
|
||||
|
||||
while (true)
|
||||
{
|
||||
Render(prompt, buffer, filtered, selectedIndex, scrollOffset, maxVisible, inputTop);
|
||||
|
||||
ConsoleKeyInfo key = Console.ReadKey(intercept: true);
|
||||
|
||||
if (key.Key == ConsoleKey.Escape)
|
||||
{
|
||||
ClearDropdown(inputTop, maxVisible);
|
||||
Console.SetCursorPosition(0, inputTop);
|
||||
Console.Write(new string(' ', prompt.Length + buffer.Length + 2));
|
||||
Console.SetCursorPosition(0, inputTop);
|
||||
return null;
|
||||
}
|
||||
else if (key.Key == ConsoleKey.Enter || key.Key == ConsoleKey.Tab)
|
||||
{
|
||||
string result = (filtered.Count > 0)
|
||||
? filtered[selectedIndex]
|
||||
: buffer;
|
||||
|
||||
ClearDropdown(inputTop, maxVisible);
|
||||
Console.SetCursorPosition(0, inputTop);
|
||||
Console.Write(prompt + result);
|
||||
Console.Write(new string(' ', Math.Max(0, buffer.Length - result.Length)));
|
||||
Console.WriteLine();
|
||||
return result;
|
||||
}
|
||||
else if (key.Key == ConsoleKey.DownArrow)
|
||||
{
|
||||
if (filtered.Count > 0)
|
||||
{
|
||||
selectedIndex = (selectedIndex + 1) % filtered.Count;
|
||||
AdjustScroll(ref scrollOffset, selectedIndex, maxVisible, filtered.Count);
|
||||
}
|
||||
}
|
||||
else if (key.Key == ConsoleKey.UpArrow)
|
||||
{
|
||||
if (filtered.Count > 0)
|
||||
{
|
||||
selectedIndex = (selectedIndex - 1 + filtered.Count) % filtered.Count;
|
||||
AdjustScroll(ref scrollOffset, selectedIndex, maxVisible, filtered.Count);
|
||||
}
|
||||
}
|
||||
else if (key.Key == ConsoleKey.Backspace)
|
||||
{
|
||||
if (buffer.Length > 0)
|
||||
{
|
||||
buffer = buffer.Substring(0, buffer.Length - 1);
|
||||
filtered = FilterAndRank(options, buffer);
|
||||
selectedIndex = 0;
|
||||
scrollOffset = 0;
|
||||
}
|
||||
}
|
||||
else if (!char.IsControl(key.KeyChar))
|
||||
{
|
||||
buffer += key.KeyChar;
|
||||
filtered = FilterAndRank(options, buffer);
|
||||
selectedIndex = 0;
|
||||
scrollOffset = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void AdjustScroll(ref int scrollOffset, int selectedIndex, int maxVisible, int total)
|
||||
{
|
||||
if (selectedIndex < scrollOffset)
|
||||
scrollOffset = selectedIndex;
|
||||
else if (selectedIndex >= scrollOffset + maxVisible)
|
||||
scrollOffset = selectedIndex - maxVisible + 1;
|
||||
|
||||
if (scrollOffset < 0) scrollOffset = 0;
|
||||
if (scrollOffset > Math.Max(0, total - maxVisible))
|
||||
scrollOffset = Math.Max(0, total - maxVisible);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Filters options to those containing the query (case-insensitive),
|
||||
/// ranking exact-prefix matches above substring matches.
|
||||
/// </summary>
|
||||
static List<string> FilterAndRank(List<string> options, string query)
|
||||
{
|
||||
if (string.IsNullOrEmpty(query))
|
||||
return options.Take(200).ToList(); // cap for display purposes
|
||||
|
||||
string q = query.ToLowerInvariant();
|
||||
|
||||
var prefixMatches = options
|
||||
.Where(o => o.StartsWith(q, StringComparison.OrdinalIgnoreCase))
|
||||
.OrderBy(o => o);
|
||||
|
||||
var containsMatches = options
|
||||
.Where(o => !o.StartsWith(q, StringComparison.OrdinalIgnoreCase)
|
||||
&& o.Contains(q, StringComparison.OrdinalIgnoreCase))
|
||||
.OrderBy(o => o);
|
||||
|
||||
return prefixMatches.Concat(containsMatches).ToList();
|
||||
}
|
||||
|
||||
static void Render(string prompt, string buffer, List<string> filtered,
|
||||
int selectedIndex, int scrollOffset, int maxVisible, int inputTop)
|
||||
{
|
||||
// --- input line ---
|
||||
Console.SetCursorPosition(0, inputTop);
|
||||
Console.Write(prompt + buffer);
|
||||
Console.Write(new string(' ', Math.Max(0, 40 - (prompt.Length + buffer.Length))));
|
||||
|
||||
// --- dropdown ---
|
||||
int visibleCount = Math.Min(maxVisible, filtered.Count);
|
||||
for (int row = 0; row < maxVisible; row++)
|
||||
{
|
||||
Console.SetCursorPosition(0, inputTop + 1 + row);
|
||||
Console.Write(new string(' ', 60)); // clear the row first
|
||||
Console.SetCursorPosition(0, inputTop + 1 + row);
|
||||
|
||||
if (row < visibleCount)
|
||||
{
|
||||
int itemIndex = scrollOffset + row;
|
||||
string item = filtered[itemIndex];
|
||||
bool isSelected = itemIndex == selectedIndex;
|
||||
|
||||
if (isSelected)
|
||||
{
|
||||
Console.BackgroundColor = ConsoleColor.DarkCyan;
|
||||
Console.ForegroundColor = ConsoleColor.Black;
|
||||
Console.Write($"> {item}");
|
||||
Console.ResetColor();
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Write($" {item}");
|
||||
}
|
||||
}
|
||||
else if (row == visibleCount && filtered.Count == 0 && buffer.Length > 0)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.DarkGray;
|
||||
Console.Write(" (no matches — Enter uses typed text as-is)");
|
||||
Console.ResetColor();
|
||||
}
|
||||
}
|
||||
|
||||
if (filtered.Count > maxVisible)
|
||||
{
|
||||
Console.SetCursorPosition(0, inputTop + 1 + maxVisible);
|
||||
Console.Write(new string(' ', 60));
|
||||
Console.SetCursorPosition(0, inputTop + 1 + maxVisible);
|
||||
Console.ForegroundColor = ConsoleColor.DarkGray;
|
||||
// Console.Write($" ...{filtered.Count - maxVisible} more (keep typing to narrow)");
|
||||
Console.ResetColor();
|
||||
}
|
||||
|
||||
// put the cursor back at the end of the typed text
|
||||
Console.SetCursorPosition(prompt.Length + buffer.Length, inputTop);
|
||||
}
|
||||
|
||||
static void ClearDropdown(int inputTop, int maxVisible)
|
||||
{
|
||||
for (int row = 0; row <= maxVisible; row++)
|
||||
{
|
||||
Console.SetCursorPosition(0, inputTop + 1 + row);
|
||||
Console.Write(new string(' ', 60));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 701 B |
Binary file not shown.
|
Before Width: | Height: | Size: 665 B |
@@ -8,4 +8,8 @@
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.12" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
Reference in New Issue
Block a user