Ideating on a library for reading

I’d like to make a library to help me with reading long texts I struggle with reading. My goal would be to have my read progress saved within SB so it gets synced, and to be able to annotate easily enough.

The texts I want to read tend to be available on places like the anarchist library, meaning there’s text and epub versions available, but I think a general solution using something like firefox’s reader view parser would be ideal.

I think perhaps the interface should be a widget that renders a single paragraph at a time, along with the current heading, and progress could be stored by saving the current heading and paragraph under that heading in the frontmatter. Additionally, a button could be added between the previous/next buttons to add an annotation, which would create a new block on that page (at the top, so pushing down earlier annotations and ending with a reverse chronological feed) that already gets annotated with the position in the text your at.

This would likely be a lot of work, so I wanted to ask for feedback on if this makes sense or if I’m missing easier ways to do this.

For me, annotating the reading position of a long document in the frontmatter and/or localstorage makes sense.

On the other hand, viewing (and, if necessary, annotating) paragraph by paragraph seems complicated to achieve for minimal gain.

But why not … within a widget. On this subject, the use of a resizable and movable frame (see Floating SidePanel/Widgets (Example: Document Explorer, Treeview, “OrbitCal” Calendar) - #19 by Mr.Red) might be useful.

Something like that
https://codepen.io/keithwyland/pen/yLyLNz or GitHub - brandly/OpenSpritz: A Speed Reading Bookmarklet
Storing position,it could be interesting to read quickly long text

…As I read, I felt like I learned to predict the next word…humans have become like LLMs…

2 Likes

What would the floating widget have? The text itself, the controls, or the annotations?

And tbh I wouldn’t be trying to get SB to also be the display of the text, I just want to so that it can automatically track where I’m at, rather than needing to count paragraphs or get another service to sync reading progress. Plus I could add a button to my index page that says how far along the text I am.

Tbh I actually don’t mind or even prefer slow reading. I like not how spritz reading doesn’t require scrolling or clicking anything, which a per paragraph display would involve a lot of.

I don’t clearly see what you are considering. The idea of ​​the floating widget is perhaps not suitable… (English is not my fluent language, I have some difficulty understanding it, even with a translation service!).

I ended up deciding to setup kravita, because it syncs reading progress and has an API I can use to show progress and potentially even pull my annotations with. For now it looks like this:

And here’s how I implemented it:

local function createPageFromTemplate(templatePage, pageName)
  -- Won't override an existing page
  if space.pageExists(pageName) then
    editor.flashNotification("Page " .. pageName .. " already exists", "error")
    return
  end
  local tpl, fm = template.fromPage(templatePage)
  local initialText = ""
  if fm.frontmatter then
    initialText = "---\n"
      .. string.trim(template.new(fm.frontmatter)())
      .. "\n---\n"
  end
  -- Write an empty page to start
  space.writePage(pageName, initialText)
  editor.navigate(pageName)
  -- Insert there, supporting |^| cursor placeholder
  editor.insertAtPos(tpl(), #initialText, true)
end

function kavita.get_currently_reading()
  local activities = kavita.fetch("/api/Series/currently-reading?PageNumber=0&PageSize=5&userId=1").body
  local rows = {}
  local baseUrl = config.get("kavita.baseUrl")

  for _, activity in ipairs(activities) do
    -- The docs don't have a good way of getting % progress
    -- https://www.kavitareader.com/docs/api
    -- We can get the overall wordCount, but can't get current progress in wordCount
    -- We can get current page, but progress on that page is just a string that doesn't map to a word count nor % of the page/chapter 
    -- (the string is "bookScrollId" from `/api/Reader/get-progress`)
    -- Instead, we'll use `/api/Reader/continue-point` to get the volume we're on to construct a link to the reader
    local continuePoint = kavita.fetch('/api/Reader/continue-point?seriesId=' .. activity.id).body
    local link = baseUrl .. '/library/' .. activity.libraryId .. '/series/' .. activity.id .. '/book/' .. continuePoint.volumeId
    local progress = activity.pagesRead / activity.pages
    local seriesMetadata = kavita.fetch('/api/Series/metadata?seriesId=' .. activity.id).body

    table.insert(rows, dom.div {
      style = "display: flex; gap: .5em",
      -- title w/ progress display
      dom.div {
        style = "position: relative; border: solid 1px var(--button-border-color); flex-grow: 1; padding: 0 .5em; border-radius: 4px; display: flex; gap: .5em; cursor: pointer;",
        onclick = function()
          if space.pageExists(activity.name) then
            editor.navigate(activity.name)
            return
          end
          createPageFromTemplate("Library/Templates/Books", activity.name)
          local updated = index.patchFrontmatter(editor.getText(),
          {
            { op="set-key", path="libraryId", value=activity.libraryId },
            { op="set-key", path="seriesId", value=activity.id },
            { op="set-key", path="volumeId", value=continuePoint.volumeId },
          })
          editor.setText(updated)
          editor.rebuildEditorState()
        end,
        dom.div {
          style = "position: absolute; top: 0; bottom: 0; left: 0; width:" .. (progress * 100) .. "%; z-index: -1; background: var(--button-background-color)"
        },
        activity.name,
        dom.span {
          style = "font-style: italic; color: var(--subtle-color);",
          seriesMetadata.writers[1].name
        }
      },
      -- button to open reader
      dom.button {
        style = 'white-space: nowrap;',
        onclick = function()
          editor.openUrl(link)
        end,
        "Read"
      }
    })
  end

  return widget.html(dom.div {
    style = "display: flex; flex-flow: column; gap: .5em",
    table.unpack(rows)
  })
end

function kavita.fetch(url, body, method)
  local baseUrl = config.get("kavita.baseUrl")
  if not baseUrl then
    error("kavita.baseUrl config not set")
  end
  local token = config.get("kavita.token")
  if not token then
    error("kavita.token config not set")
  end
  return net.proxyFetch(baseUrl .. url, {
    method = method or "GET",
    headers = {
      ["x-api-key"] = token,
      ["Content-Type"] = "application/json"
    },
    body = body
  })
end
1 Like