init: this is working pretty well so far

This commit is contained in:
Tobias Davis 2023-08-16 21:21:48 -05:00
commit ebc86de6ef
7 changed files with 298 additions and 0 deletions

13
.editorconfig Normal file
View file

@ -0,0 +1,13 @@
root = true
[*]
charset = utf-8
indent_style = tab
indent_size = 4
[*.js]
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = true

9
LICENSE.md Normal file
View file

@ -0,0 +1,9 @@
Very Open License (VOL)
The contributor(s) to this creative work voluntarily grant permission
to any individual(s) or entities of any kind
- to use the creative work in any manner,
- to modify the creative work without restriction,
- to sell the creative work or derivatives thereof for profit, and
- to release modifications of the creative work in part or whole under any license
with no requirement for compensation or recognition of any kind.

75
README.md Normal file
View file

@ -0,0 +1,75 @@
# Obsidian `@People`
Obsidian plugin to add that familiar @-to-tag-someone syntax:
![](./example.png)
When you hit enter on a suggestion, it'll create a link that looks like this:
```
The author was [[@Rich Hickey]]
```
and leave the cursor at the end.
## Options
There's not a lot to configure here, but they are important:
### 1. Where are the people files?
You probably want to group the people files in a folder.
I usually do something like this:
```
People/
@Rich Hickey.md
@Rich Harris.md
```
You can configure that in settings to point to somewhere else, like `Reference/People/` or whatever makes sense.
### 2. Explicit link structure?
By default, the plugin will insert the simple version:
```
[[@Rich Hickey]]
```
But you might rather make that explicit, in which case you can enable "explicit links" and they'll look like this instead:
```
[[People/@Rich Hickey.md|@Rich Hickey]]
```
### 3. Last name grouping?
For my personal Obsidian vaults, I have a lot of people with my same last name, so I put them in sub-folders for organization.
You can toggle the "last name folder" option, and it'll do that in the links.
The earlier example folder structure would be:
```
People/
Hickey/
@Rich Hickey.md
Harris/
@Rich Harris.md
```
And then the inserted link would look like:
```
[[People/Hickey/@Rich Hickey.md|@Rich Hickey]]
```
> Note: figuring out what the "last name" is (or if it even has one) is really complicated! This plugin takes a very simply approach: if you split a name by the space character, it'll just pick the last "word". So for example "Charles Le Fabre" would be "Fabre" and *not* "Le Fabre".
>
> I'm open to better implementations that don't add a lot of complexity, just start a discussion.
## License
Published and made available freely under the [Very Open License](http://veryopenlicense.com/).

BIN
example.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

167
main.js Normal file
View file

@ -0,0 +1,167 @@
const { App, Editor, EditorSuggest, TFile, Notice, Plugin, PluginSettingTab, Setting } = require('obsidian')
const DEFAULT_SETTINGS = {
peopleFolder: 'People/',
// Defaults:
// useExplicitLinks: undefined,
// useLastNameFolder: undefined,
}
const NAME_REGEX = /\/@([^\/]+)\.md$/
const LAST_NAME_REGEX = /([\S]+)$/
const getPersonName = (filename, settings) => filename.startsWith(settings.peopleFolder)
&& filename.endsWith('.md')
&& filename.includes('/@')
&& NAME_REGEX.exec(filename)?.[1]
module.exports = class AtPeople extends Plugin {
async onload() {
await this.loadSettings()
this.registerEvent(this.app.vault.on('delete', async event => { await this.update(event) }))
this.registerEvent(this.app.vault.on('create', async event => { await this.update(event) }))
this.registerEvent(this.app.vault.on('rename', async (event, originalFilepath) => { await this.update(event, originalFilepath) }))
this.app.workspace.onLayoutReady(this.initialize)
this.addSettingTab(new AtPeopleSettingTab(this.app, this))
this.suggestor = new AtPeopleSuggestor(this.app, this.settings)
this.registerEditorSuggest(this.suggestor)
}
async loadSettings() {
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData())
}
async saveSettings() {
await this.saveData(this.settings || DEFAULT_SETTINGS)
}
updatePeopleMap = () => {
this.suggestor.updatePeopleMap(this.peopleFileMap)
}
update = async ({ path, deleted, ...remaining }, originalFilepath) => {
this.peopleFileMap = this.peopleFileMap || {}
const name = getPersonName(path, this.settings)
let needsUpdated
if (name) {
this.peopleFileMap[name] = path
needsUpdated = true
}
originalFilepath = originalFilepath && getPersonName(originalFilepath, this.settings)
if (originalFilepath) {
delete this.peopleFileMap[originalFilepath]
needsUpdated = true
}
if (needsUpdated) this.updatePeopleMap()
}
initialize = () => {
this.peopleFileMap = {}
for (const filename in this.app.vault.fileMap) {
const name = getPersonName(filename, this.settings)
if (name) this.peopleFileMap[name] = filename
}
this.updatePeopleMap()
}
}
class AtPeopleSuggestor extends EditorSuggest {
constructor(app, settings) {
super(app)
this.settings = settings
}
updatePeopleMap(peopleFileMap) {
this.peopleFileMap = peopleFileMap
}
onTrigger(cursor, editor, tFile) {
let charsLeftOfCursor = editor.getLine(cursor.line).substring(0, cursor.ch)
let atIndex = charsLeftOfCursor.lastIndexOf('@')
let query = atIndex >= 0 && charsLeftOfCursor.substring(atIndex + 1)
if (query && !query.includes(']]')) {
return {
start: { line: cursor.line, ch: atIndex },
end: { line: cursor.line, ch: cursor.ch },
query,
}
}
return null
}
getSuggestions(context) {
let suggestions = []
for (let key in (this.peopleFileMap || {}))
if (key.toLowerCase().startsWith(context.query))
suggestions.push({
suggestionType: 'set',
displayText: key,
context,
})
suggestions.push({
suggestionType: 'create',
displayText: context.query,
context,
})
return suggestions
}
renderSuggestion(value, elem) {
if (value.suggestionType === 'create') elem.setText('New person: ' + value.displayText)
else elem.setText(value.displayText)
}
selectSuggestion(value) {
let link
if (this.settings.useExplicitLinks && this.settings.useLastNameFolder) {
let lastName = LAST_NAME_REGEX.exec(value.displayText)
lastName = lastName && lastName[1] && (lastName[1] + '/') || ''
link = `[[${this.settings.peopleFolder}${lastName}@${value.displayText}.md|@${value.displayText}]]`
} else if (this.settings.useExplicitLinks && !this.settings.useLastNameFolder) {
link = `[[${this.settings.peopleFolder}@${value.displayText}.md|@${value.displayText}]]`
} else {
link = `[[@${value.displayText}]]`
}
value.context.editor.replaceRange(
link,
value.context.start,
value.context.end,
)
}
}
class AtPeopleSettingTab extends PluginSettingTab {
constructor(app, plugin) {
super(app, plugin)
this.plugin = plugin
}
display() {
const { containerEl } = this
containerEl.empty()
new Setting(containerEl)
.setName('People folder')
.setDesc('The folder where people files live, e.g. "People/". (With trailing slash.)')
.addText(
text => text
.setPlaceholder(DEFAULT_SETTINGS.peopleFolder)
.setValue(this.plugin.settings.peopleFolder)
.onChange(async (value) => {
this.plugin.settings.peopleFolder = value
await this.plugin.saveSettings()
})
)
new Setting(containerEl)
.setName('Explicit links')
.setDesc('When inserting links include the full path, e.g. [[People/@Bob Dole.md|@Bob Dole]]')
.addToggle(
toggle => toggle.onChange(async (value) => {
this.plugin.settings.useExplicitLinks = value
await this.plugin.saveSettings()
})
)
new Setting(containerEl)
.setName('Last name folder')
.setDesc('When using explicit links, use the "last name" (the last non-spaced word) as a sub-folder, e.g. [[People/Dole/@Bob Dole.md|@Bob Dole]]')
.addToggle(
toggle => toggle.onChange(async (value) => {
this.plugin.settings.useLastNameFolder = value
await this.plugin.saveSettings()
})
)
}
}

9
manifest.json Normal file
View file

@ -0,0 +1,9 @@
{
"id": "at-people",
"name": "At People",
"version": "1.0.0",
"description": "Use the @ to create links to people files.",
"author": "saibotsivad",
"authorUrl": "https://davistobias.com",
"isDesktopOnly": false
}

25
package.json Normal file
View file

@ -0,0 +1,25 @@
{
"name": "obsidian-at-people",
"version": "1.0.0",
"description": "Use the @ to create links to people files.",
"main": "main.js",
"repository": {
"type": "git",
"url": "git+https://github.com/saibotsivad/obsidian-at-people.git"
},
"keywords": [
"obsidian",
"obsidian-plugin",
"people"
],
"author": {
"name": "Tobias Davis",
"email": "tobias@davistobias.com",
"url": "https://davistobias.com"
},
"license": "See license in LICENSE.md",
"bugs": {
"url": "https://github.com/saibotsivad/obsidian-at-people/issues"
},
"homepage": "https://github.com/saibotsivad/obsidian-at-people#readme"
}