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 ancestor
result = get_toot(ml_from_url(result.ancestors[1].url))
end
return result
end
love.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 becomes
visible.
set_cursor = function(cursor_id)
-- position Cursor on the specific toot at Url
Cursor_node = Root
if Nodes[cursor_id] then
Cursor_node = Nodes[cursor_id]
end
end
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 columns
Surface = {}
-- we're going to be computing box heights
love.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 needed
for _,x in pairs(Nodes) do
if x.ntracks == nil then
x.ntracks = compute_ntracks(x)
end
end
-- prepare the tracks
-- each track is 600px + 20px of gutter between nodes
render_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_filename
local 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 here
love.graphics.clear(love.graphics.getBackgroundColor())
love.graphics.print('loading '..Url..'...', 5,5)
love.graphics.present()
-- load JSON from Url
local initial_ml = ml_from_url(Url)
local thread = get_toot(initial_ml)
if thread.ancestors and #thread.ancestors > 0 then
-- redo with oldest ancestor
love.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))
end
load_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 columns
Surface = {}
-- we're going to be computing box heights
love.graphics.setFont(love.graphics.newFont(scale(20)))
-- compute number of tracks needed
for _,x in pairs(Nodes) do
if x.ntracks == nil then
x.ntracks = compute_ntracks(x)
end
end
-- prepare the tracks
-- each track is 600px + 20px of gutter between nodes
render_node_and_descendants(Root.id, --[[y]] 0, --[[xlo]] 0, --[[xhi]] 620 * Root.ntracks)
end
print('A')
if Major_version >= 12 then
-- reload Url from network
if Url then
-- indicate some progress
-- load can take some time, and it's synchronous, so we'll draw to screen right here
love.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 = Root
if Nodes[initial_ml.id] then
Cursor_node = Nodes[initial_ml.id]
end
ensure_cursor_node_within_viewport()
end
B()
else
-- LÖVE pre-v12 has no https
-- reload same thread from file
if Input_filename then
love.graphics.setFont(love.graphics.newFont(scale(20))) -- editor objects implicitly depend on current font
local f = io.open(Input_filename)
assert(f)
local thread_data = json.decode(f:read('*a'))
f:close()
render_thread_to_surface(thread_data)
end
B()
end
-- load Nodes to Surface
love.graphics.setFont(love.graphics.newFont(scale(20))) -- editor objects implicitly depend on current font
render_nodes_to_surface()
B()