mirror of
https://github.com/stracker-phil/obsidian-mention-things.git
synced 2026-07-22 05:43:27 +00:00
init: this is working pretty well so far
This commit is contained in:
commit
ebc86de6ef
7 changed files with 298 additions and 0 deletions
13
.editorconfig
Normal file
13
.editorconfig
Normal 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
9
LICENSE.md
Normal 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
75
README.md
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
# Obsidian `@People`
|
||||
|
||||
Obsidian plugin to add that familiar @-to-tag-someone syntax:
|
||||
|
||||

|
||||
|
||||
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
BIN
example.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
167
main.js
Normal file
167
main.js
Normal 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
9
manifest.json
Normal 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
25
package.json
Normal 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"
|
||||
}
|
||||
Loading…
Reference in a new issue