I'm taking this opportunity to significantly clean up the data flow. It's easy to forget quite what A and B are supposed to do, and so to pile on additional responsibilities on them:
In particular, A is not intended to modify the inputs that go into rebuilding Surface, in this app Nodes.
Perhaps I shouldn't call it A, but M or something to leave lots of space for phases before A. For now, hopefully a comment will suffice.
3 major code paths:
FTM3CSOAZMHEWXORH2GG3XJZWHCVG6EAUDI4MSGM6IEU2NN5RNPQC QFOQPDDW2C7TDPDNT67VU76HGZGTFXQY52R3BNONU5C4F5NYPAOQC SJY3GBOCOLWAOPSZHMPMUFH6Z5SG25KFH7FBOFZ2QOH4YZZVN23AC CS3LJYX6TH6JPPG5C6QNLEQXOIOOD7YWDFYBMPTATQUCYJOE4DOQC R5QXEHUIZLELJGGCZAE7ATNS3CLRJ7JFRENMGH4XXH24C5WABZDQC TQUVV6ZZAXGVEFRBUHRXFUIZYVBT5VFGUKSM25MYWS3QF6OB4VEQC VSBSWTE4IVQDRXLPQ7VTDIIEBEF7GMGRBHZ2IA73ZR6B2KZWI5JAC BJ5X5O4ACBBJ56LRBBSTCW6IBQP4HAEOOOPNH3SKTA4F66YTOIDAC H2KZUAWHQXTRUFDPDH4AUG6EP3OR5U3GGFPVGTCY2XJB3WOEO7TQC HMH2GYB6BLH4HE7N6J6ZK57RKFICWXSU3KJL7EGYTXJ5DC5XHBIQC 3RLPFCICVQARJBY26A4SX3JZCQWMGPOZC5TDIKHJ75M4EPKJVAWQC QDXO357AHOOUH3RHQFIF6XHLWOEGZFCSGUHPIL6QIQNY53CPNXFAC BF7TW3EKRIDYC6J2Q2J4YOBAVQF55Y3H6KGZIHNXMH4N72MR6GXQC X7HYGAL2QVKG7M5EMZ2VSH37UYWGE3EPUXYQBJOVL6IGJFZ2I5AAC try_load_nodes_from_url = function(initial_ml)local result = get_toot(initial_ml)if result.ancestors and #result.ancestors > 0 then-- redo with oldest ancestorresult = get_toot(ml_from_url(result.ancestors[1].url))endreturn resultendlove.graphics.clear(love.graphics.getBackgroundColor())love.graphics.print('reloading from earliest ancestor '..result.ancestors[1].url..'...', 5,5)love.graphics.present()
- run app using LÖVE v11 and `lua unfurl.lua` shim. Ensure cursor is on correct node and visible.- run app using LÖVE v12. Ensure cursor is on correct node and visible.Paste in a new thread using `ctrl+v`. Ensure cursor is on correct node and visible.- pan around. Ensure cursor node can go out of viewport.- Press `ctrl`+arrow keys. Ensure cursor moves to correct node and becomesvisible.
set_cursor = function(cursor_id)-- position Cursor on the specific toot at UrlCursor_node = Rootif Nodes[cursor_id] thenCursor_node = Nodes[cursor_id]endend
render_thread_to_surface = function(thread_data)-- side effects: Nodes, Root-- design constraints:-- trees only, no graphs or DAGs-- parents above children-- a child shares its parent's column exactly only if it's an only child-- parents can overlap columns with children and more distant descendants-- siblings never share a column-- siblings never overlap columns-- siblings always occupy the same row-- cousins/aunts/nephews never overlap columnsSurface = {}-- we're going to be computing box heightslove.graphics.setFont(love.graphics.newFont(scale(20)))-- compute mapping from ids to nodes
load_nodes_from_json = function(thread_data, cursor_id)-- massage JSON into a table from id to node
-- compute number of tracks neededfor _,x in pairs(Nodes) doif x.ntracks == nil thenx.ntracks = compute_ntracks(x)endend-- prepare the tracks-- each track is 600px + 20px of gutter between nodesrender_node_and_descendants(Root.id, --[[y]] 0, --[[xlo]] 0, --[[xhi]] 620 * Root.ntracks)end
end
load_nodes_from_input_file = function()-- side effects: Nodes, Root, Cursor_node-- load JSON from Input_filenamelocal f = io.open(Input_filename)assert(f)local thread = json.decode(f:read('*a'))f:close()load_nodes_from_json(thread)end
load_nodes_from_url = function()-- side effects: Nodes, Root, Cursor_node-- indicate some progress-- load can take some time, and it's synchronous, so we'll draw to screen right herelove.graphics.clear(love.graphics.getBackgroundColor())love.graphics.print('loading '..Url..'...', 5,5)love.graphics.present()-- load JSON from Urllocal initial_ml = ml_from_url(Url)local thread = get_toot(initial_ml)if thread.ancestors and #thread.ancestors > 0 then-- redo with oldest ancestorlove.graphics.clear(love.graphics.getBackgroundColor())love.graphics.print('reloading from earliest ancestor '..thread.ancestors[1].url..'...', 5,5)love.graphics.present()thread = get_toot(ml_from_url(thread.ancestors[1].url))endload_nodes_from_json(thread, initial_ml.id)end
render_nodes_to_surface = function()-- design constraints:-- trees only, no graphs or DAGs-- parents above children-- a child shares its parent's column exactly only if it's an only child-- parents can overlap columns with children and more distant descendants-- siblings never share a column-- siblings never overlap columns-- siblings always occupy the same row-- cousins/aunts/nephews never overlap columnsSurface = {}-- we're going to be computing box heightslove.graphics.setFont(love.graphics.newFont(scale(20)))-- compute number of tracks neededfor _,x in pairs(Nodes) doif x.ntracks == nil thenx.ntracks = compute_ntracks(x)endend-- prepare the tracks-- each track is 600px + 20px of gutter between nodesrender_node_and_descendants(Root.id, --[[y]] 0, --[[xlo]] 0, --[[xhi]] 620 * Root.ntracks)end
print('A')if Major_version >= 12 then-- reload Url from networkif Url then-- indicate some progress-- load can take some time, and it's synchronous, so we'll draw to screen right herelove.graphics.clear(love.graphics.getBackgroundColor())love.graphics.print('loading '..Url..'...', 5,5)love.graphics.present()local initial_ml = ml_from_url(Url)local thread_data = try_load_nodes_from_url(initial_ml)render_thread_to_surface(thread_data)Cursor_node = Rootif Nodes[initial_ml.id] thenCursor_node = Nodes[initial_ml.id]endensure_cursor_node_within_viewport()endB()else-- LÖVE pre-v12 has no https-- reload same thread from fileif Input_filename thenlove.graphics.setFont(love.graphics.newFont(scale(20))) -- editor objects implicitly depend on current fontlocal f = io.open(Input_filename)assert(f)local thread_data = json.decode(f:read('*a'))f:close()render_thread_to_surface(thread_data)endB()end
-- load Nodes to Surfacelove.graphics.setFont(love.graphics.newFont(scale(20))) -- editor objects implicitly depend on current fontrender_nodes_to_surface()B()