Object, Text data and UI

Hi SB community!. I wanted to request some guidance and also share my approach to improve my workflow and "personal preference dislikes" that I have when I'm on mobile.

Dislike 1: Long (and dynamic) frontmatter at the top of the page. I have to scroll instead of start writing, or my case... pushing the buttons.

Dislike 2: After saving the data to the frontmatter (from button's onclick with the typical patchFrontmatter and editor rebuild/reload) the page auto-scroll a bit, loosing the previous position/focus. Again I have to scroll.

My approach to improve my workflow:

In general, I think that scroll issues are related to text changing above the current position, specially if values/text are long and the screen is small (phone). For instance, wikilinks tend to move/wrap two lines of the screen.

  1. Top Hook (*). Works fine for my buttons (no auto-scrool after editor save/rebuild/reload). Long frontmatter between top/buttons and page text is not ideal. Also, not able to write text (Top Hook is a widget, not a input text section).
  2. Object data (the fenced code starting as ```#my_data). Instead of saving my data on the frontmatter, I saved as object as markdown/text (the source of truth) at the end of the page (kinda backmatter!). Works fine with some restrictions (only one object per page) and fancy functions to find the object (LIQ returns only pos and not toPos (**)), convert from/to text/yaml and replace text range. My issue: On other page sections (likes tables), LIQ returns the updated data after a few seconds/full UI page reload (probably because page index completion?). I "solved" searching the data as text/yaml instead of LIQ, which I think is not ideal.
  3. Save the data as text items with lot of attributes. toPos is available from LIQ, so I think easy to replace/update but I suspect I will suffer the same “delayed data refresh” on other LIQ section of the page.

So, now a few questions:

  • Is there a way to update a object programmatically (like index.patchFrontmatter() but for objects)? I mean as markdown/text, not at the datastore level (index.indexObjects(page, objects)). I created (***)
  • In widgets functions, how to wait for datastore page index completion or some message/event? I even prefer to not show any data if the index is not totally updated.

Thank you and excuse me for this long.

(*)

function topWidgetsButtons()
    local text = [[
Top Hook: ${os.date()}
]]
  local pageText = editor.getText()
  local fm = index.extractFrontmatter(pageText)
  local page = editor.getCurrentPath()

  if fm.frontmatter and page.startsWith("XYZ") then
    local items = {}
    table.insert(items, dom.text {"### some titles"})
    table.insert(items, drawSomeButtons())
    table.insert(items, dom.text {" --- " })

    return widget.htmlBlock( dom.div { table.unpack(items) })
  end

  return widget.new { }
end

event.listen {
  name = "hooks:renderTopWidgets",
  run = function(e)
    return topWidgetsButtons()
  end
}
#sb-main .cm-editor .sb-lua-top-widget .content, #sb-main .cm-editor .sb-lua-bottom-widget .content {
  max-height: 100%;
  padding: 1px;
}

(**)

data_tag is the object type, ie: #person. without the #

function c_obj.getObjectFromPage(data_tag, page)
  local pos_start, pos_end, finding_pos = nil, nil, nil
  local content = { }
  if not data_tag then return nil end
  
  local qry_page = page and page or editor.getCurrentPage()
  currentText = editor.getText()
  
  local qry = query[[from tags[data_tag] where page==qry_page and tag==data_tag order by pos]]
  --print("qry", qry)
  
  if #qry == 0 then
    return nil
  else
    pos_start = qry[1].pos
    finding_pos, pos_end = string.find(currentText, "```" , pos_start+4)
    if pos_end then
      local section = string.sub(currentText, pos_start, pos_end)
      content = c_obj.convertStringToTable(section) --section:match("^[^\n]*\n(.-)\n[^\n]*$")
      --print(pos_start, pos_end, section, type(content), content)
    end
    --editor.setSelection(pos_start, pos_end)
    end
  local page_ref = qry_page .. "@" .. pos_start
  return { ref = page_ref, obj=content, pos=pos_start, toPos=pos_end }

end

(***)

function c_obj.updateObject(data_tag, path, value)
  local dummy_obj = { 
    metadata = { created_by="c_obj.updateObject()", created_at=c_core.getDateTime() },
    data = {},
  }

  local row = c_obj.getObjectFromPage(data_tag)
  local text = editor.getText()
  local updated = {}
  if row==nil then
    dummy_obj.data[path] = value
    updated = c_obj.convertTableToMarkdown(dummy_obj, data_tag)
    editor.insertAtPos("\n" .. updated, #text)
  else
    row.obj.data[path] = value
    updated = c_obj.convertTableToMarkdown(row.obj, data_tag)
    editor.replaceRange(row.pos, row.toPos, updated)
  end
  editor.save()
  editor.rebuildEditorState()
  editor.reloadPage()
end

function c_obj.convertTableToMarkdown(tbl, data_tag)
  --created by LLM....
  local obj_tag = data_tag and data_tag or "c_obj/unknown"
  local yaml_body = c_obj.convertYamlToString(tbl)
  return "```#" .. obj_tag .. "\n" .. yaml_body .. "\n```"
end

function c_obj.convertYamlToString(tbl, indent_level)
  --created by LLM....long to post here
end

function c_obj.convertStringToTable(str)
  --created by LLM....long to post here
end