I’m writing a novel in Obsidian, but what I’m really missed here is Scrivener-like info panel, where I can see all the info related to the scene or the whole project, like synopsis, words progress, list of characters, etc. So I decided to create one myself with the help of dataviewjs and share it with other people.
How to set it up: make sure you have Dataview plugin installed and javascript queries turned on. Copy this code to the separate file and pin this file to the sidebar.
```dataviewjs
const createProgressBar = (char_count, char_goal, words_count, words_goal) => {
let percents = 0
if (char_goal != undefined) {
percents = Math.round(char_count / char_goal * 100)
} else if (words_goal != undefined) {
percents = Math.round(words_count / words_goal * 100)
}
dv.paragraph("<progress max=100 value=" + percents + "> </progress> " + percents + "%")
}
const getFirstSplit = (split) => {
if (split.type == "tabs") {
return split
} else if (split.type == "split") {
return getFirstSplit(split.children[0])
}
}
let firstSplit = getFirstSplit(app.workspace.rootSplit)
let current = firstSplit.children[firstSplit.currentTab].view.file
if (current != null) {
let page = dv.page(current.path)
if (page != undefined) {
let char_goal = page.char_goal
let words_goal = page.words_goal
let synopsis = page.synopsis
let characters = page.characters
let tags = page.tags
let project = page.project
let highlights
if (project != undefined) {
dv.header(4, "[[" + project + "]]")
}
if (synopsis != undefined) {
dv.span("***")
dv.header(5, "Synopsis")
dv.span(synopsis)
}
if (page.type == "writing scene") {
let content = await dv.io.load(current.path)
highlights = content.match(/==.*?==/g)
content = content.replace(/^---\n.*?\n---/ms, "").trim().replaceAll("—", "").replaceAll(/[\n]+/mg, " ").replaceAll(/[ ]+/mg, " ").replaceAll("==", "").replaceAll("*", "").replaceAll("#", "")
let words = content.split(" ")
dv.span("***")
dv.header(5, "Scene progress")
if (words_goal != undefined) {
dv.paragraph(words.length + " / " + words_goal + " words")
}
if (char_goal != undefined) {
dv.paragraph(content.length + " / " + char_goal + " characters")
}
createProgressBar(content.length, char_goal, words.length, words_goal)
}
if (page.type == "writing project") {
let path = '"' + page.file.folder + '"'
let scenePages = dv.pages(path).filter(p => p.type == "writing scene")
let words_count = 0
let char_count = 0
let scenesData = []
for (let scene of scenePages) {
let sceneContent = await dv.io.load(scene.file.path)
sceneContent = sceneContent.replace(/^---\n.*?\n---/ms, "").trim().replaceAll("—", "").replaceAll(/[\n]+/mg, " ").replaceAll(/[ ]+/mg, " ").replaceAll("==", "").replaceAll("*", "").replaceAll("#", "")
let sceneWords = sceneContent.split(" ")
words_count = words_count + sceneWords.length
char_count = char_count + sceneContent.length
scenesData.push({words_count: sceneWords.length, char_count: sceneContent.length, link: scene.file.link, words_goal: scene.words_goal, char_goal: scene.char_goal})
}
dv.span("***")
dv.header(5, "Novel progress")
if (words_goal != undefined) {
dv.paragraph(words_count + " / " + words_goal + " words")
}
if (char_goal != undefined) {
dv.paragraph(char_count + " / " + char_goal + " characters")
}
createProgressBar(char_count, char_goal, words_count, words_goal)
dv.span("***")
dv.header(5, "Scenes progress")
scenesData.forEach(scene => {
dv.span(scene.link)
createProgressBar(scene.char_count, scene.char_goal, scene.words_count, scene.words_goal)
})
}
if (characters != undefined) {
dv.span("***")
dv.header(5, "Characters")
dv.span(characters.map(c => " [[" + c + "]]") + "")
}
if (tags != undefined) {
dv.span("***")
dv.header(5, "Tags")
dv.span(tags.map(c => " #" + c) + "")
}
if (highlights != undefined) {
highlights = highlights.map(h => h.replaceAll("==", ""))
dv.span("***")
dv.header(5, "Highlights")
dv.paragraph(highlights)
}
}
}
```
Next you need to set up your novel folder. Create a new folder for the novel anywhere. Inside of this folder you can create files of two types: scene notes or project notes. In scene notes you will write your text, and the project note is the place to accumulate info about the whole novel.
In scene notes you can write this metadata:
---
type: writing scene
project: My New Novel
words_goal: 2400
char_goal: 15000
synopsis: Something interesting happens in this scene.
characters:
- John
- Mary
tags:
- fantasy
- unfinished
---
where project is the name of your novel, words_goal and char_goal are the goals for the current scene (you can use both or only one of them, if you wish), synopsis, characters and tags are optional. Feel free to change anything except for the type, the type is important for the script to work.
You need only one project note. Put it in the root of your folder and give it the same name as your novel, for links to work correctly. In project note you can write this metedata:
---
type: writing project
words_goal: 48000
char_goal: 300000
synopsis: There is a novel about some characters doing some things.
characters:
- John
- Mary
- Peter
tags:
- fantasy
- unfinished
---
In this case the goals and all other metadata are related to the whole novel.
Now if you open any of your scene notes along with the pinned note in the sidebar, you will see something like this:
And if you open the project note, you will see this:
As you can see, in the project note info you can see progress for every scene and also accumulated progress for the whole novel. Everything written in the non-scene files does not count. It is also easy enough to tweak the code to add your own metadata.
It is worth mentioning that the info panel shows info for the first pane in your workspace, so if you open several files in splitted view, you will see the info for the top left one.