Directory Structure Graphical Treefication

JavaScript (ES6), 237 128 bytes

f=(s,r=`.\n`+s.replace(/^ */gm,"$&└── "),q=r.replace(/^(.*)( |└)(?=.+\n\1[|└])/m,(_,i,j)=>i+`|├`[+(j>' ')]))=>q==r?r:f(s,q)

Where \n represents the literal newline character. Explanation: r is created from s by prepending the . line and inserting the └── at the end of each line's indent. This is now correct for the last line of the input, but each must be "extended" upwards as far as possible. This is the job of q, which searches for a and recursively replaces the spaces directly above it with |s unless it reaches another which gets turned into instead. The recursion ends when no further replacements can be made. Note that if the character above the is a space or a then the text to the left of the is always the same as that on the previous line so I can just use \1 to test that the one character is above the other.


Retina, 88 bytes

m`^ *
$&├── 
 {4}
|   
T+`|├` └`(?<=(.)*).(?!.+¶(?>(?<-1>.)*)[|├└])
^
.¶

Try it online!

I suppose I could technically count this as one byte per character by swapping out some characters, reading the source as ISO 8859-1 and then finding a single-byte encoding for the output which contains and , but I can't be bothered to work out the details right now. (For the record, that would be 72 bytes.)

Explanation

Stage 1: Substitution

m`^ *
$&├── 

We start by matching the indentation on each line and inserting ├──.

Stage 2: Substitution

 {4}
|   

Next, we match every group of 4 spaces and replace the first with a |. Now all that needs fixing is | that go to the bottom of the output and that should be . Both of those cases can be recognised by looking at the character directly below the one we potentially want to change.

Stage 3: Transliteration

T+`|├` └`(?<=(.)*).(?!.+¶(?>(?<-1>.)*)[|├└])

The (?<=(.)*) counts how many characters precede the match on the current line to measure it's horizontal position. Then the lookahead skips to the next line with .+¶, matches as many characters as we've captured in group 1 with (?>(?<-1>.)*) (to advance to the same horizontal position) and then checks whether the next character (i.e. the one below the actual match) is one of |├└. If that's the case, the match fails, and in all other cases it succeeds and the stage substitutes spaces for | and for .

This won't fix all characters in a single run, so we apply this stage repeatedly with the + option until the output stops changing.

Stage 4: Substitution

^
.¶

All that's left is the first line, so we simply match the beginning of the string and prepend a . and a linefeed.