prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  "Save configuration" Improve UX
  *Feature Request*
  
  I have some time in the next week, I may try and implement this depending on how many roadblocks I hit. Currently, to save a configuration, you are prompted to enter a directory. If it isn't valid, you have to scratch your head wondering what the exact paths are, because you can't discover any directory without exiting `archinstall`. 
  
  It would be great if there was a sort of tab completion, or even filesystem traversal similar to `ncdu` to find a directory to save the configuration in.
  
  </issue>
  <code>
  [start of archinstall/lib/user_interaction/save_conf.py]
  1 from __future__ import annotations
  2 
  3 from pathlib import Path
  4 from typing import Any, Dict, TYPE_CHECKING
  5 
  6 from ..configuration import ConfigurationOutput
  7 from ..menu import Menu
  8 from ..menu.menu import MenuSelectionType
  9 from ..output import log
  10 
  11 if TYPE_CHECKING:
  12 	_: Any
  13 
  14 
  15 def save_config(config: Dict):
  16 
  17 	def preview(selection: str):
  18 		if options['user_config'] == selection:
  19 			json_config = config_output.user_config_to_json()
  20 			return f'{config_output.user_configuration_file}\n{json_config}'
  21 		elif options['user_creds'] == selection:
  22 			if json_config := config_output.user_credentials_to_json():
  23 				return f'{config_output.user_credentials_file}\n{json_config}'
  24 			else:
  25 				return str(_('No configuration'))
  26 		elif options['disk_layout'] == selection:
  27 			if json_config := config_output.disk_layout_to_json():
  28 				return f'{config_output.disk_layout_file}\n{json_config}'
  29 			else:
  30 				return str(_('No configuration'))
  31 		elif options['all'] == selection:
  32 			output = f'{config_output.user_configuration_file}\n'
  33 			if json_config := config_output.user_credentials_to_json():
  34 				output += f'{config_output.user_credentials_file}\n'
  35 			if json_config := config_output.disk_layout_to_json():
  36 				output += f'{config_output.disk_layout_file}\n'
  37 			return output[:-1]
  38 		return None
  39 
  40 	config_output = ConfigurationOutput(config)
  41 
  42 	options = {
  43 		'user_config': str(_('Save user configuration')),
  44 		'user_creds': str(_('Save user credentials')),
  45 		'disk_layout': str(_('Save disk layout')),
  46 		'all': str(_('Save all'))
  47 	}
  48 
  49 	choice = Menu(
  50 		_('Choose which configuration to save'),
  51 		list(options.values()),
  52 		sort=False,
  53 		skip=True,
  54 		preview_size=0.75,
  55 		preview_command=preview
  56 	).run()
  57 
  58 	if choice.type_ == MenuSelectionType.Skip:
  59 		return
  60 
  61 	while True:
  62 		path = input(_('Enter a directory for the configuration(s) to be saved: ')).strip(' ')
  63 		dest_path = Path(path)
  64 		if dest_path.exists() and dest_path.is_dir():
  65 			break
  66 		log(_('Not a valid directory: {}').format(dest_path), fg='red')
  67 
  68 	if options['user_config'] == choice.value:
  69 		config_output.save_user_config(dest_path)
  70 	elif options['user_creds'] == choice.value:
  71 		config_output.save_user_creds(dest_path)
  72 	elif options['disk_layout'] == choice.value:
  73 		config_output.save_disk_layout(dest_path)
  74 	elif options['all'] == choice.value:
  75 		config_output.save_user_config(dest_path)
  76 		config_output.save_user_creds(dest_path)
  77 		config_output.save_disk_layout(dest_path)
  78
  [end of archinstall/lib/user_interaction/save_conf.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: diff --git a/archinstall/lib/user_interaction/save_conf.py b/archinstall/lib/user_interaction/save_conf.py
  --- a/archinstall/lib/user_interaction/save_conf.py
  +++ b/archinstall/lib/user_interaction/save_conf.py
  @@ -1,9 +1,12 @@
   from __future__ import annotations
   
  +import logging
  +
   from pathlib import Path
   from typing import Any, Dict, TYPE_CHECKING
   
   from ..configuration import ConfigurationOutput
  +from ..general import SysCommand
   from ..menu import Menu
   from ..menu.menu import MenuSelectionType
   from ..output import log
  @@ -58,20 +61,75 @@
   	if choice.type_ == MenuSelectionType.Skip:
   		return
   
  -	while True:
  -		path = input(_('Enter a directory for the configuration(s) to be saved: ')).strip(' ')
  -		dest_path = Path(path)
  -		if dest_path.exists() and dest_path.is_dir():
  -			break
  -		log(_('Not a valid directory: {}').format(dest_path), fg='red')
  -
  -	if options['user_config'] == choice.value:
  -		config_output.save_user_config(dest_path)
  -	elif options['user_creds'] == choice.value:
  -		config_output.save_user_creds(dest_path)
  -	elif options['disk_layout'] == choice.value:
  -		config_output.save_disk_layout(dest_path)
  -	elif options['all'] == choice.value:
  -		config_output.save_user_config(dest_path)
  -		config_output.save_user_creds(dest_path)
  -		config_output.save_disk_layout(dest_path)
  +	dirs_to_exclude = [
  +		'/bin',
  +		'/dev',
  +		'/lib',
  +		'/lib64',
  +		'/lost+found',
  +		'/opt',
  +		'/proc',
  +		'/run',
  +		'/sbin',
  +		'/srv',
  +		'/sys',
  +		'/usr',
  +		'/var',
  +	]
  +	log(
  +		_('When picking a directory to save configuration files to,'
  +		' by default we will ignore the following folders: ') + ','.join(dirs_to_exclude),
  +		level=logging.DEBUG
  +	)
  +
  +	log(_('Finding possible directories to save configuration files ...'), level=logging.INFO)
  +	
  +	find_exclude = '-path ' + ' -prune -o -path '.join(dirs_to_exclude) + ' -prune '
  +	file_picker_command = f'find / {find_exclude} -o -type d -print0'
  +	possible_save_dirs = list(
  +		filter(None, SysCommand(file_picker_command).decode().split('\x00'))
  +	)
  +
  +	selection = Menu(
  +		_('Select directory (or directories) for saving configuration files'),
  +		possible_save_dirs,
  +		multi=True,
  +		skip=True,
  +		allow_reset=False,
  +	).run()
  +
  +	match selection.type_:
  +		case MenuSelectionType.Skip:
  +			return
  +		case _:
  +			save_dirs = selection.value
  +
  +	prompt = _('Do you want to save {} configuration file(s) in the following locations?\n\n{}').format(
  +		list(options.keys())[list(options.values()).index(choice.value)],
  +		save_dirs
  +	)
  +	save_confirmation = Menu(prompt, Menu.yes_no(), default_option=Menu.yes()).run()
  +	if save_confirmation == Menu.no():
  +		return
  +	
  +	log(
  +		_('Saving {} configuration files to {}').format(
  +			list(options.keys())[list(options.values()).index(choice.value)],
  +			save_dirs
  +		),
  +		level=logging.DEBUG
  +	)
  +	
  +	if save_dirs is not None:
  +		for save_dir_str in save_dirs:
  +			save_dir = Path(save_dir_str)
  +			if options['user_config'] == choice.value:
  +				config_output.save_user_config(save_dir)
  +			elif options['user_creds'] == choice.value:
  +				config_output.save_user_creds(save_dir)
  +			elif options['disk_layout'] == choice.value:
  +				config_output.save_disk_layout(save_dir)
  +			elif options['all'] == choice.value:
  +				config_output.save_user_config(save_dir)
  +				config_output.save_user_creds(save_dir)
  +				config_output.save_disk_layout(save_dir)
  
verification_info: {"golden_diff": "diff --git a/archinstall/lib/user_interaction/save_conf.py b/archinstall/lib/user_interaction/save_conf.py\n--- a/archinstall/lib/user_interaction/save_conf.py\n+++ b/archinstall/lib/user_interaction/save_conf.py\n@@ -1,9 +1,12 @@\n from __future__ import annotations\n \n+import logging\n+\n from pathlib import Path\n from typing import Any, Dict, TYPE_CHECKING\n \n from ..configuration import ConfigurationOutput\n+from ..general import SysCommand\n from ..menu import Menu\n from ..menu.menu import MenuSelectionType\n from ..output import log\n@@ -58,20 +61,75 @@\n \tif choice.type_ == MenuSelectionType.Skip:\n \t\treturn\n \n-\twhile True:\n-\t\tpath = input(_('Enter a directory for the configuration(s) to be saved: ')).strip(' ')\n-\t\tdest_path = Path(path)\n-\t\tif dest_path.exists() and dest_path.is_dir():\n-\t\t\tbreak\n-\t\tlog(_('Not a valid directory: {}').format(dest_path), fg='red')\n-\n-\tif options['user_config'] == choice.value:\n-\t\tconfig_output.save_user_config(dest_path)\n-\telif options['user_creds'] == choice.value:\n-\t\tconfig_output.save_user_creds(dest_path)\n-\telif options['disk_layout'] == choice.value:\n-\t\tconfig_output.save_disk_layout(dest_path)\n-\telif options['all'] == choice.value:\n-\t\tconfig_output.save_user_config(dest_path)\n-\t\tconfig_output.save_user_creds(dest_path)\n-\t\tconfig_output.save_disk_layout(dest_path)\n+\tdirs_to_exclude = [\n+\t\t'/bin',\n+\t\t'/dev',\n+\t\t'/lib',\n+\t\t'/lib64',\n+\t\t'/lost+found',\n+\t\t'/opt',\n+\t\t'/proc',\n+\t\t'/run',\n+\t\t'/sbin',\n+\t\t'/srv',\n+\t\t'/sys',\n+\t\t'/usr',\n+\t\t'/var',\n+\t]\n+\tlog(\n+\t\t_('When picking a directory to save configuration files to,'\n+\t\t' by default we will ignore the following folders: ') + ','.join(dirs_to_exclude),\n+\t\tlevel=logging.DEBUG\n+\t)\n+\n+\tlog(_('Finding possible directories to save configuration files ...'), level=logging.INFO)\n+\t\n+\tfind_exclude = '-path ' + ' -prune -o -path '.join(dirs_to_exclude) + ' -prune '\n+\tfile_picker_command = f'find / {find_exclude} -o -type d -print0'\n+\tpossible_save_dirs = list(\n+\t\tfilter(None, SysCommand(file_picker_command).decode().split('\\x00'))\n+\t)\n+\n+\tselection = Menu(\n+\t\t_('Select directory (or directories) for saving configuration files'),\n+\t\tpossible_save_dirs,\n+\t\tmulti=True,\n+\t\tskip=True,\n+\t\tallow_reset=False,\n+\t).run()\n+\n+\tmatch selection.type_:\n+\t\tcase MenuSelectionType.Skip:\n+\t\t\treturn\n+\t\tcase _:\n+\t\t\tsave_dirs = selection.value\n+\n+\tprompt = _('Do you want to save {} configuration file(s) in the following locations?\\n\\n{}').format(\n+\t\tlist(options.keys())[list(options.values()).index(choice.value)],\n+\t\tsave_dirs\n+\t)\n+\tsave_confirmation = Menu(prompt, Menu.yes_no(), default_option=Menu.yes()).run()\n+\tif save_confirmation == Menu.no():\n+\t\treturn\n+\t\n+\tlog(\n+\t\t_('Saving {} configuration files to {}').format(\n+\t\t\tlist(options.keys())[list(options.values()).index(choice.value)],\n+\t\t\tsave_dirs\n+\t\t),\n+\t\tlevel=logging.DEBUG\n+\t)\n+\t\n+\tif save_dirs is not None:\n+\t\tfor save_dir_str in save_dirs:\n+\t\t\tsave_dir = Path(save_dir_str)\n+\t\t\tif options['user_config'] == choice.value:\n+\t\t\t\tconfig_output.save_user_config(save_dir)\n+\t\t\telif options['user_creds'] == choice.value:\n+\t\t\t\tconfig_output.save_user_creds(save_dir)\n+\t\t\telif options['disk_layout'] == choice.value:\n+\t\t\t\tconfig_output.save_disk_layout(save_dir)\n+\t\t\telif options['all'] == choice.value:\n+\t\t\t\tconfig_output.save_user_config(save_dir)\n+\t\t\t\tconfig_output.save_user_creds(save_dir)\n+\t\t\t\tconfig_output.save_disk_layout(save_dir)\n", "issue": "\"Save configuration\" Improve UX\n*Feature Request*\r\n\r\nI have some time in the next week, I may try and implement this depending on how many roadblocks I hit. Currently, to save a configuration, you are prompted to enter a directory. If it isn't valid, you have to scratch your head wondering what the exact paths are, because you can't discover any directory without exiting `archinstall`. \r\n\r\nIt would be great if there was a sort of tab completion, or even filesystem traversal similar to `ncdu` to find a directory to save the configuration in.\n", "before_files": [{"content": "from __future__ import annotations\n\nfrom pathlib import Path\nfrom typing import Any, Dict, TYPE_CHECKING\n\nfrom ..configuration import ConfigurationOutput\nfrom ..menu import Menu\nfrom ..menu.menu import MenuSelectionType\nfrom ..output import log\n\nif TYPE_CHECKING:\n\t_: Any\n\n\ndef save_config(config: Dict):\n\n\tdef preview(selection: str):\n\t\tif options['user_config'] == selection:\n\t\t\tjson_config = config_output.user_config_to_json()\n\t\t\treturn f'{config_output.user_configuration_file}\\n{json_config}'\n\t\telif options['user_creds'] == selection:\n\t\t\tif json_config := config_output.user_credentials_to_json():\n\t\t\t\treturn f'{config_output.user_credentials_file}\\n{json_config}'\n\t\t\telse:\n\t\t\t\treturn str(_('No configuration'))\n\t\telif options['disk_layout'] == selection:\n\t\t\tif json_config := config_output.disk_layout_to_json():\n\t\t\t\treturn f'{config_output.disk_layout_file}\\n{json_config}'\n\t\t\telse:\n\t\t\t\treturn str(_('No configuration'))\n\t\telif options['all'] == selection:\n\t\t\toutput = f'{config_output.user_configuration_file}\\n'\n\t\t\tif json_config := config_output.user_credentials_to_json():\n\t\t\t\toutput += f'{config_output.user_credentials_file}\\n'\n\t\t\tif json_config := config_output.disk_layout_to_json():\n\t\t\t\toutput += f'{config_output.disk_layout_file}\\n'\n\t\t\treturn output[:-1]\n\t\treturn None\n\n\tconfig_output = ConfigurationOutput(config)\n\n\toptions = {\n\t\t'user_config': str(_('Save user configuration')),\n\t\t'user_creds': str(_('Save user credentials')),\n\t\t'disk_layout': str(_('Save disk layout')),\n\t\t'all': str(_('Save all'))\n\t}\n\n\tchoice = Menu(\n\t\t_('Choose which configuration to save'),\n\t\tlist(options.values()),\n\t\tsort=False,\n\t\tskip=True,\n\t\tpreview_size=0.75,\n\t\tpreview_command=preview\n\t).run()\n\n\tif choice.type_ == MenuSelectionType.Skip:\n\t\treturn\n\n\twhile True:\n\t\tpath = input(_('Enter a directory for the configuration(s) to be saved: ')).strip(' ')\n\t\tdest_path = Path(path)\n\t\tif dest_path.exists() and dest_path.is_dir():\n\t\t\tbreak\n\t\tlog(_('Not a valid directory: {}').format(dest_path), fg='red')\n\n\tif options['user_config'] == choice.value:\n\t\tconfig_output.save_user_config(dest_path)\n\telif options['user_creds'] == choice.value:\n\t\tconfig_output.save_user_creds(dest_path)\n\telif options['disk_layout'] == choice.value:\n\t\tconfig_output.save_disk_layout(dest_path)\n\telif options['all'] == choice.value:\n\t\tconfig_output.save_user_config(dest_path)\n\t\tconfig_output.save_user_creds(dest_path)\n\t\tconfig_output.save_disk_layout(dest_path)\n", "path": "archinstall/lib/user_interaction/save_conf.py"}]}