├── .github
├── FUNDING.yml
└── ISSUE_TEMPLATE
│ ├── bug-report.md
│ └── feature-request.md
├── .gitignore
├── .vscode
└── tasks.json
├── LICENSE
├── README.md
├── cmd
├── create
│ └── main.go
├── csearch
│ └── main.go
├── link
│ └── main.go
├── local-test
│ └── main.go
├── search
│ └── main.go
├── setcursor
│ └── main.go
├── toc
│ └── main.go
└── wiki-link
│ └── main.go
├── core
├── core.go
└── core_test.go
├── db
├── db.go
└── db_test.go
├── doc
├── Authorize.png
├── BasicSearch.png
├── CreateSearch1.png
├── CreateSearch2.png
├── Link1.png
├── Link2.png
├── NewNote.png
├── RecentNotes.png
├── SearchQueryInBearApp.png
├── SearchSpecialInBearApp.png
├── SpecialSearchAutocomplete.png
├── TagAnyOrder.png
├── TagAutocomplete.png
├── TagAutocompleteMultiple.png
├── TagAutocompleteSearch.png
├── TagFilter.png
└── TagTextSearch.png
├── go.mod
├── go.sum
├── icon.png
└── info.plist
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: drgrib
4 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug-report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug Report
3 | about: Bug Report
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | Have you followed all the installation instructions here?
11 | https://github.com/drgrib/alfred-bear/blob/master/README.md#install
12 |
13 | If you already have and it still isn't working, please provide the debug log you get by following these steps:
14 | https://github.com/drgrib/alfred-bear/issues/56#issuecomment-1546733678
15 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature-request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature Request
3 | about: Feature Request
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | cmd/create/create
3 | cmd/csearch/csearch
4 | cmd/link/link
5 | cmd/search/search
6 | cmd/setcursor/setcursor
7 | cmd/toc/toc
8 | cmd/wiki-link/wiki-link
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | // See https://go.microsoft.com/fwlink/?LinkId=733558
3 | // for the documentation about the tasks.json format
4 | "version": "2.0.0",
5 | "tasks": [
6 | {
7 | "label": "go build all",
8 | "dependsOn": [
9 | "go build csearch",
10 | "go build search",
11 | "go build link",
12 | "go build toc",
13 | "go build create",
14 | "go build setcursor",
15 | "go build wiki-link",
16 | ],
17 | "group": "build"
18 | },
19 | {
20 | "label": "go build csearch",
21 | "type": "shell",
22 | "options": {
23 | "cwd": "${workspaceFolder}/cmd/csearch"
24 | },
25 | "command": "rm -f csearch; go build",
26 | "presentation": {
27 | "focus": true,
28 | },
29 | "group": "build",
30 | "problemMatcher": []
31 | },
32 | {
33 | "label": "go build search",
34 | "type": "shell",
35 | "options": {
36 | "cwd": "${workspaceFolder}/cmd/search"
37 | },
38 | "command": "rm -f search; go build",
39 | "presentation": {
40 | "focus": true,
41 | },
42 | "group": "build",
43 | "problemMatcher": []
44 | },
45 | {
46 | "label": "go build link",
47 | "type": "shell",
48 | "options": {
49 | "cwd": "${workspaceFolder}/cmd/link"
50 | },
51 | "command": "rm -f link; go build",
52 | "presentation": {
53 | "focus": true,
54 | },
55 | "group": "build",
56 | "problemMatcher": []
57 | },
58 | {
59 | "label": "go build toc",
60 | "type": "shell",
61 | "options": {
62 | "cwd": "${workspaceFolder}/cmd/toc"
63 | },
64 | "command": "rm -f toc; go build",
65 | "presentation": {
66 | "focus": true,
67 | },
68 | "group": "build",
69 | "problemMatcher": []
70 | },
71 | {
72 | "label": "go build create",
73 | "type": "shell",
74 | "options": {
75 | "cwd": "${workspaceFolder}/cmd/create"
76 | },
77 | "command": "rm -f create; go build",
78 | "presentation": {
79 | "focus": true,
80 | },
81 | "group": "build",
82 | "problemMatcher": []
83 | },
84 | {
85 | "label": "go build setcursor",
86 | "type": "shell",
87 | "options": {
88 | "cwd": "${workspaceFolder}/cmd/setcursor"
89 | },
90 | "command": "rm -f setcursor; go build",
91 | "presentation": {
92 | "focus": true,
93 | },
94 | "group": "build",
95 | "problemMatcher": []
96 | },
97 | {
98 | "label": "go build wiki-link",
99 | "type": "shell",
100 | "options": {
101 | "cwd": "${workspaceFolder}/cmd/wiki-link"
102 | },
103 | "command": "rm -f wiki-link; go build",
104 | "presentation": {
105 | "focus": true,
106 | },
107 | "group": "build",
108 | "problemMatcher": []
109 | },
110 | ]
111 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Chris Redford
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://www.paypal.com/donate?business=N2GLXLS5KBFBY&item_name=Chris+Redford¤cy_code=USD)
2 |
3 | # Bear Workflow
4 |
5 | Streamlined note searching and creation for [Bear](http://www.bear-writer.com/) using [Alfred](https://www.alfredapp.com/workflows/).
6 |
7 | ## Install
8 |
9 | 1. [Download](https://github.com/drgrib/alfred-bear/releases/download/v1.2.4/Bear.alfredworkflow) the latest release and double-click _Bear.alfredworkflow_. Alfred will open the workflow and install it.
10 | 2. [Authorize all the executables used by the workflow](#authorize-all-executables).
11 | 3. If you are on an Apple Silicon Mac (M1, M2, etc.), [install Rosetta](https://github.com/drgrib/alfred-bear#install-rosetta)
12 |
13 | If you are using an old version of Bear before 2.0 use [this](https://github.com/drgrib/alfred-bear/releases/download/1.1.9/Bear.alfredworkflow) download link for the latest version supporting it.
14 |
15 | ## Search
16 |
17 | `bs` or `bsearch`
18 |
19 | ### Recent Notes
20 |
21 | Leave the search field empty to see recent notes with their tags as subtitles.
22 |
23 |
24 |
25 | ### Basic Search
26 |
27 | Start typing to search through the titles and text of most recent notes, title matches first.
28 |
29 |
30 |
31 | ### Tag Search
32 |
33 | Type `#` at any time to autocomplete your tags.
34 |
35 |
36 |
37 | Start typing to search tags.
38 |
39 |
40 |
41 | Once completed, the notes will be filtered by that tag.
42 |
43 |
44 |
45 | Add more tags to filter by multiple tags.
46 |
47 |
48 |
49 | Start typing to search titles and text within a tag.
50 |
51 |
52 |
53 | All these terms can be typed in any order and they will work the same. For example, if you want to add a tag after typing a bare search term, the autocomplete will still help you. Or if you remember you want to filter by another tag after typing the first tag and a bare search term, you can autocomplete and add the second tag by typing `#` again.
54 |
55 |
56 |
57 | ### Search in Bear App
58 |
59 | You can search any _query_ you type in the Bear app's main window by holding down the option key. If you've entered a tag, it will open the Bear main window to that tag for further browsing.
60 |
61 |
62 |
63 | ### Open Note in Bear App
64 |
65 | Similarly, you can open any _note_ you select in the Bear app's main window by holding down the command key.
66 |
67 | ### Link Pasting
68 |
69 | While in your Bear notes, you can paste a link to another note by searching for it and holding down the shift key.
70 |
71 |
72 |
73 |
74 | ### Table of Contents Pasting
75 |
76 | Similarly, you can paste a table of contents for a note by searching for it and holding down the command and shift keys.
77 |
78 | ## New Notes
79 |
80 | `bn` or `bnew` followed by title and optional tags.
81 |
82 |
83 |
84 | Tag autocomplete works the same. Also, any text in your clipboard can be added to the new note by holding down the command key.
85 |
86 | ## Create/Search
87 |
88 | `bcs` or `bcsearch`
89 |
90 | You may find sometimes that you want to retrieve a note if it exists and create it if it does not. This command provides that functionality by combining the behavior of search and create. It will provide all the same search results as normal search and additionally add a create item third in the list using normal create options.
91 |
92 |
93 |
94 | If there are less than two search items, the create item will be the last or only item.
95 |
96 |
97 |
98 | You can additionally create links to notes by holding the shift key while selecting a search item. Selecting the create item while holding the shift key will do nothing.
99 |
100 | ## Why I created this
101 |
102 | I am especially grateful to Chris Brown, who created a [Python based Bear workflow](https://github.com/chrisbro/alfred-bear). It was the basis for this project. However, I decided to create my own project for a few reasons:
103 |
104 | - Compiled Go is faster than interpretted Python. Not that much faster but fast enough for me to notice when searching and creating notes throughout the day.
105 | - I wanted the features involving tag searching and autocompletion, link pasting, and automatic clipboard note content.
106 | - I wanted fewer, more optimized SQL queries into the Bear database to increase speed since this appears to be the main bottleneck on performance.
107 |
108 | ## Authorization
109 |
110 | The first time you use the workflow after installing or upgrading, you will see a security warning:
111 |
112 |
113 |
114 | This is a quirk of macOS 10.15 and above. Apple currently forces developers to pay $99 a year to be able to officially sign their executables and avoid this warning, which I'm not going to pay since I'm providing this workflow for free as an open source project.
115 |
116 | After seeing this warning, you have to go to **System Preferences > Security & Privacy > General** and click the new button that has appeared to allow the executable to run. You then have to run it again and you will see this security warning _again_ but now it will have a new button that lets you allow the executable to run.
117 |
118 | These warnings will appear once for each of the executables inside the workflow as you use new features. Once you have authorized all of them, you won't see these warnings anymore until you install a new version.
119 |
120 | ### Authorize All Executables
121 |
122 | If you want to authorize all Bear Workflow executables at once or if you do not see the above security warnings but the workflow isn't working, you can do the following:
123 |
124 | 1. Go to the **Workflows** section in Alfred Preferences
125 | 2. Right click on **Bear** _by drgrib_ and select **Open in Terminal**
126 | 3. Copy this command and execute it:
127 |
128 | ```
129 | xattr -rd com.apple.quarantine cmd
130 | ```
131 |
132 | This should authorize all the Alfred Bear the executables and fix the security errors.
133 |
134 | If you are trying to use the Bear workflow loaded from prefrences via cloud (e.g. through Google Drive) on a new Mac, you may also need to run this command, which will make the executables executable:
135 | ```
136 | find . -mindepth 3 -maxdepth 3 -type f \! -name "*.go" -exec chmod +x {} \;
137 | ```
138 |
139 | ## Install Rosetta
140 |
141 | If your mac is based on an Apple Silicon chip (M1, M2, etc.), you need to have Rosetta installed on your system, otherwise Alfred workflows will fail silently.
142 |
143 | Copy this command and execute in terminal to install Rosetta:
144 |
145 | ```sh
146 | softwareupdate --install-rosetta
147 | ```
148 |
149 | If that fails, try this version, which requires your admin password:
150 |
151 | ```
152 | sudo softwareupdate --install-rosetta --agree-to-license
153 | ```
154 |
--------------------------------------------------------------------------------
/cmd/create/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "os"
5 |
6 | "github.com/drgrib/alfred"
7 |
8 | "github.com/drgrib/alfred-bear/core"
9 | "github.com/drgrib/alfred-bear/db"
10 | )
11 |
12 | func main() {
13 | query := core.ParseQuery(os.Args[1])
14 |
15 | litedb, err := db.NewBearDB()
16 | if err != nil {
17 | panic(err)
18 | }
19 |
20 | autocompleted, err := core.AutocompleteTags(litedb, query)
21 | if err != nil {
22 | panic(err)
23 | }
24 |
25 | if !autocompleted {
26 | item, err := core.GetCreateItem(query)
27 | if err != nil {
28 | panic(err)
29 | }
30 | alfred.Add(*item)
31 | }
32 |
33 | alfred.Run()
34 | }
35 |
--------------------------------------------------------------------------------
/cmd/csearch/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "log"
5 | "os"
6 | "strings"
7 |
8 | "github.com/drgrib/alfred"
9 |
10 | "github.com/drgrib/alfred-bear/core"
11 | "github.com/drgrib/alfred-bear/db"
12 | )
13 |
14 | func main() {
15 | log.SetFlags(log.Flags() &^ (log.Ldate | log.Ltime))
16 |
17 | createIndex := 2
18 | query := core.ParseQuery(os.Args[1])
19 |
20 | litedb, err := db.NewBearDB()
21 | if err != nil {
22 | log.Fatalf("%+v", err)
23 | }
24 |
25 | autocompleted, err := core.Autocomplete(litedb, query)
26 | if err != nil {
27 | log.Fatalf("%+v", err)
28 | }
29 |
30 | if !autocompleted {
31 | searchRows, err := core.GetSearchRows(litedb, query)
32 | if err != nil {
33 | log.Fatalf("%+v", err)
34 | }
35 |
36 | appSearchItem, err := core.GetAppSearchItem(query)
37 | if err != nil {
38 | log.Fatalf("%+v", err)
39 | }
40 |
41 | createItem, err := core.GetCreateItem(query)
42 | if err != nil {
43 | log.Fatalf("%+v", err)
44 | }
45 |
46 | if len(searchRows) > 0 {
47 | endIndex := createIndex
48 | if len(searchRows) < createIndex {
49 | endIndex = len(searchRows)
50 | }
51 | for _, row := range searchRows[:endIndex] {
52 | alfred.Add(core.RowToItem(row, query))
53 | }
54 | } else if strings.Contains(query.WordString, "@") {
55 | alfred.Add(*appSearchItem)
56 | }
57 | alfred.Add(*createItem)
58 | if len(searchRows) > createIndex {
59 | for _, row := range searchRows[createIndex:] {
60 | alfred.Add(core.RowToItem(row, query))
61 | }
62 | }
63 | }
64 |
65 | alfred.Run()
66 | }
67 |
--------------------------------------------------------------------------------
/cmd/link/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "strings"
7 |
8 | "golang.org/x/text/unicode/norm"
9 |
10 | "github.com/drgrib/alfred-bear/db"
11 | )
12 |
13 | func main() {
14 | noteID := norm.NFC.String(os.Args[1])
15 |
16 | litedb, err := db.NewBearDB()
17 | if err != nil {
18 | panic(err)
19 | }
20 |
21 | callback := fmt.Sprintf("bear://x-callback-url/open-note?id=%s&show_window=yes&new_window=yes", noteID)
22 | rows, err := litedb.Query(fmt.Sprintf(db.NOTE_TITLE_BY_ID, noteID))
23 | if err != nil {
24 | panic(err)
25 | }
26 | title := rows[0][db.TitleKey]
27 | title = strings.ReplaceAll(title, "[", "- ")
28 | title = strings.ReplaceAll(title, "]", " -")
29 | link := fmt.Sprintf("[%s](%s)", title, callback)
30 |
31 | fmt.Print(link)
32 | }
33 |
--------------------------------------------------------------------------------
/cmd/local-test/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "log"
7 | "strings"
8 | "text/template"
9 |
10 | "github.com/drgrib/alfred-bear/db"
11 | )
12 |
13 | type TagQueryArg struct {
14 | Text string
15 | IntersectQuery string
16 | }
17 |
18 | func TemplateToString(templateStr string, data any) (string, error) {
19 | var buffer bytes.Buffer
20 | t := template.Must(template.New("").Parse(templateStr))
21 | err := t.Execute(&buffer, data)
22 | return buffer.String(), err
23 | }
24 |
25 | func main() {
26 | log.SetFlags(log.Flags() &^ (log.Ldate | log.Ltime))
27 |
28 | litedb, err := db.NewBearDB()
29 | if err != nil {
30 | log.Fatalf("%+v", err)
31 | }
32 |
33 | targetTags := []string{"#health/sleep", "#media/youtube"}
34 | notes, err := litedb.QueryNotesByTextAndTags("", targetTags)
35 | if err != nil {
36 | log.Fatalf("%+v", err)
37 | }
38 |
39 | for _, n := range notes {
40 | tags := strings.Split(n[db.TagsKey], ",")
41 | found := map[string]bool{}
42 | for _, t := range tags {
43 | found[t] = true
44 | }
45 | invalid := false
46 | for _, t := range targetTags {
47 | if !found[db.RemoveTagHashes(t)] {
48 | invalid = true
49 | }
50 | }
51 | if invalid {
52 | fmt.Print("INVALID ")
53 | }
54 | fmt.Println(n[db.TitleKey], n[db.TagsKey])
55 | }
56 |
57 | fmt.Println()
58 |
59 | queryTemplate := `
60 | WITH
61 | joined AS (
62 | SELECT
63 | note.ZUNIQUEIDENTIFIER,
64 | note.ZTITLE,
65 | note.ZTEXT,
66 | note.ZMODIFICATIONDATE,
67 | tag.ZTITLE AS TAG_TITLE,
68 | images.ZSEARCHTEXT
69 | FROM
70 | ZSFNOTE note
71 | INNER JOIN
72 | Z_5TAGS nTag ON note.Z_PK = nTag.Z_5NOTES
73 | INNER JOIN
74 | ZSFNOTETAG tag ON nTag.Z_13TAGS = tag.Z_PK
75 | LEFT JOIN
76 | ZSFNOTEFILE images ON images.ZNOTE = note.Z_PK
77 | WHERE
78 | note.ZARCHIVED = 0
79 | AND note.ZTRASHED = 0
80 | AND note.ZTEXT IS NOT NULL
81 | ),
82 | hasSearchedTags AS (
83 | {{ .IntersectQuery}}
84 | )
85 | SELECT
86 | ZUNIQUEIDENTIFIER,
87 | ZTITLE,
88 | GROUP_CONCAT(DISTINCT TAG_TITLE) AS TAGS
89 | FROM
90 | joined
91 | WHERE
92 | ZUNIQUEIDENTIFIER IN hasSearchedTags
93 | AND (
94 | utflower(ZTITLE) LIKE utflower('%{{ .Text}}%') OR
95 | utflower(ZTEXT) LIKE utflower('%{{ .Text}}%') OR
96 | ZSEARCHTEXT LIKE utflower('%{{ .Text}}%')
97 | )
98 | GROUP BY
99 | ZUNIQUEIDENTIFIER,
100 | ZTITLE
101 | ORDER BY
102 | CASE WHEN utflower(ZTITLE) LIKE utflower('%{{ .Text}}%') THEN 0 ELSE 1 END,
103 | ZMODIFICATIONDATE DESC
104 | LIMIT 20
105 | `
106 |
107 | var selectStatements []string
108 | for _, t := range targetTags {
109 | s := fmt.Sprintf("SELECT ZUNIQUEIDENTIFIER FROM joined WHERE utflower(TAG_TITLE) = utflower('%s')", db.RemoveTagHashes(t))
110 | selectStatements = append(selectStatements, s)
111 | }
112 |
113 | tagQueryArg := TagQueryArg{
114 | Text: "",
115 | IntersectQuery: strings.Join(selectStatements, "\nINTERSECT\n"),
116 | }
117 |
118 | query, err := TemplateToString(queryTemplate, tagQueryArg)
119 | if err != nil {
120 | log.Fatalf("%+v", err)
121 | }
122 |
123 | notes, err = litedb.Query(query)
124 | if err != nil {
125 | log.Fatalf("%+v", err)
126 | }
127 |
128 | for _, n := range notes {
129 | fmt.Println(n[db.TitleKey], n[db.TagsKey])
130 | }
131 |
132 | fmt.Println(tagQueryArg.IntersectQuery)
133 | }
134 |
--------------------------------------------------------------------------------
/cmd/search/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "os"
5 | "strings"
6 |
7 | "github.com/drgrib/alfred"
8 |
9 | "github.com/drgrib/alfred-bear/core"
10 | "github.com/drgrib/alfred-bear/db"
11 | )
12 |
13 | func main() {
14 | query := core.ParseQuery(os.Args[1])
15 |
16 | litedb, err := db.NewBearDB()
17 | if err != nil {
18 | panic(err)
19 | }
20 |
21 | autocompleted, err := core.Autocomplete(litedb, query)
22 | if err != nil {
23 | panic(err)
24 | }
25 |
26 | if !autocompleted {
27 | rows, err := core.GetSearchRows(litedb, query)
28 | if err != nil {
29 | panic(err)
30 | }
31 | core.AddNoteRowsToAlfred(rows, query)
32 | if len(rows) == 0 {
33 | if strings.Contains(query.WordString, "@") {
34 | mainWindowItem, err := core.GetAppSearchItem(query)
35 | if err != nil {
36 | panic(err)
37 | }
38 | alfred.Add(*mainWindowItem)
39 | } else {
40 | alfred.Add(alfred.Item{
41 | Title: "No matching items found",
42 | Valid: alfred.Bool(false),
43 | })
44 | }
45 | }
46 | }
47 |
48 | alfred.Run()
49 | }
50 |
--------------------------------------------------------------------------------
/cmd/setcursor/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "errors"
5 | "time"
6 |
7 | "github.com/drgrib/mac"
8 | )
9 |
10 | func main() {
11 | timeoutChan := time.After(10 * time.Second)
12 | loop:
13 | for {
14 | select {
15 | case <-timeoutChan:
16 | panic(errors.New("timed out without Bear activating"))
17 | default:
18 | application, err := mac.GetFrontMostApplication()
19 | if err != nil {
20 | panic(err)
21 | }
22 | if application == "Bear" {
23 | break loop
24 | }
25 | time.Sleep(50 * time.Millisecond)
26 | }
27 | }
28 |
29 | script := `
30 | tell application "System Events"
31 | tell process "Bear"
32 | key code 126 using {command down}
33 | end tell
34 | end tell
35 | `
36 | _, err := mac.RunApplescript(script)
37 | if err != nil {
38 | panic(err)
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/cmd/toc/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "net/url"
6 | "os"
7 | "regexp"
8 | "strings"
9 |
10 | "golang.org/x/text/unicode/norm"
11 |
12 | "github.com/drgrib/alfred-bear/db"
13 | )
14 |
15 | func main() {
16 | noteID := norm.NFC.String(os.Args[1])
17 |
18 | litedb, err := db.NewBearDB()
19 | if err != nil {
20 | panic(err)
21 | }
22 |
23 | rows, err := litedb.Query(fmt.Sprintf(db.NOTE_TEXT_BY_ID, noteID))
24 | if err != nil {
25 | panic(err)
26 | }
27 | text := rows[0][db.TextKey]
28 |
29 | headerRe := regexp.MustCompile(`^#+ .+`)
30 | var tocLines []string
31 | textLines := strings.Split(text, "\n")
32 | for _, l := range textLines {
33 | if headerRe.MatchString(l) {
34 | hashCount := 0
35 | for _, r := range l {
36 | if r != '#' {
37 | break
38 | }
39 | hashCount++
40 | }
41 |
42 | header := strings.TrimLeft(l, "# ")
43 | escaped := url.PathEscape(header)
44 | callback := fmt.Sprintf(
45 | "bear://x-callback-url/open-note?id=%s&header=%s&show_window=yes&new_window=yes", noteID, escaped)
46 |
47 | header = strings.ReplaceAll(header, "[", "- ")
48 | header = strings.ReplaceAll(header, "]", " -")
49 |
50 | tocLines = append(tocLines,
51 | fmt.Sprintf("%s* [%s](%s)", strings.Repeat("\t", hashCount-1), header, callback),
52 | )
53 | }
54 | }
55 |
56 | toc := strings.Join(tocLines, "\n")
57 | fmt.Print(toc)
58 | }
59 |
--------------------------------------------------------------------------------
/cmd/wiki-link/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "strings"
7 |
8 | "golang.org/x/text/unicode/norm"
9 |
10 | "github.com/drgrib/alfred-bear/db"
11 | )
12 |
13 | func main() {
14 | noteID := norm.NFC.String(os.Args[1])
15 |
16 | litedb, err := db.NewBearDB()
17 | if err != nil {
18 | panic(err)
19 | }
20 |
21 | rows, err := litedb.Query(fmt.Sprintf(db.NOTE_TITLE_BY_ID, noteID))
22 | if err != nil {
23 | panic(err)
24 | }
25 | title := rows[0][db.TitleKey]
26 | title = strings.ReplaceAll(title, "[", `\[`)
27 | title = strings.ReplaceAll(title, "]", `\]`)
28 | title = strings.ReplaceAll(title, "/", `\/`)
29 | link := fmt.Sprintf("[[%s]]", title)
30 |
31 | fmt.Print(link)
32 | }
33 |
--------------------------------------------------------------------------------
/core/core.go:
--------------------------------------------------------------------------------
1 | package core
2 |
3 | import (
4 | "fmt"
5 | "net/url"
6 | "regexp"
7 | "sort"
8 | "strings"
9 |
10 | "github.com/atotto/clipboard"
11 | "github.com/drgrib/alfred"
12 | "github.com/pkg/errors"
13 | "golang.org/x/text/unicode/norm"
14 |
15 | "github.com/drgrib/alfred-bear/db"
16 | )
17 |
18 | var special = []string{
19 | "@tagged",
20 | "@untagged",
21 | "@today",
22 | "@yesterday",
23 | "@lastXdays",
24 | "@images",
25 | "@files",
26 | "@attachments",
27 | "@task",
28 | "@todo",
29 | "@done",
30 | "@code",
31 | "@title",
32 | "@locked",
33 | "@date(",
34 | "@cdate(",
35 | }
36 |
37 | const argSplit = "|"
38 |
39 | func getUniqueTagString(tagString string) string {
40 | if tagString == "" {
41 | return ""
42 | }
43 | tags := strings.Split(tagString, ",")
44 | uniqueTags := []string{}
45 | for _, t := range tags {
46 | isPrefix := false
47 | for _, other := range tags {
48 | if t != other && strings.HasPrefix(other, t) {
49 | isPrefix = true
50 | break
51 | }
52 | }
53 | if !isPrefix {
54 | // Multiword tag.
55 | if strings.Contains(t, " ") {
56 | t += "#"
57 | }
58 | uniqueTags = append(uniqueTags, t)
59 | }
60 | }
61 | sort.Strings(uniqueTags)
62 | return "#" + strings.Join(uniqueTags, " #")
63 | }
64 |
65 | func RowToItem(row db.Note, query Query) alfred.Item {
66 | searchCallbackString := getSearchCallbackString(query)
67 | return alfred.Item{
68 | Title: row[db.TitleKey],
69 | Subtitle: getUniqueTagString(row[db.TagsKey]),
70 | Arg: strings.Join([]string{
71 | row[db.NoteIDKey],
72 | searchCallbackString,
73 | },
74 | argSplit,
75 | ),
76 | Valid: alfred.Bool(true),
77 | }
78 | }
79 |
80 | func AddNoteRowsToAlfred(rows []db.Note, query Query) {
81 | for _, row := range rows {
82 | item := RowToItem(row, query)
83 | alfred.Add(item)
84 | }
85 | }
86 |
87 | type Query struct {
88 | Tokens []string
89 | Tags []string
90 | LastToken string
91 | WordString string
92 | }
93 |
94 | func (query Query) String() string {
95 | return strings.Join(query.Tokens, " ")
96 | }
97 |
98 | var spaces = regexp.MustCompile(`\s+`) //nolint:gochecknoglobals
99 |
100 | func ParseQuery(arg string) Query {
101 | query := Query{Tokens: spaces.Split(norm.NFC.String(arg), -1)}
102 |
103 | var words []string
104 | var buffer []string
105 | tagStarted := false
106 | for _, t := range query.Tokens {
107 | switch {
108 | case strings.HasSuffix(t, "#"):
109 | if tagStarted {
110 | // Add the token to the buffer and record tag.
111 | // #a multiword tag#
112 | buffer = append(buffer, t)
113 | tag := strings.Join(buffer, " ")
114 | query.Tags = append(query.Tags, tag)
115 | buffer = nil
116 | tagStarted = false
117 | } else {
118 | words = append(words, t)
119 | }
120 | case strings.HasPrefix(t, "#"):
121 | if tagStarted {
122 | // Split the non-tag tokens from previous tag and
123 | // restart buffer with new token.
124 | // #tag1 some some words #tag2
125 | query.Tags = append(query.Tags, buffer[0])
126 | words = append(words, buffer[1:]...)
127 | buffer = []string{t}
128 | } else {
129 | buffer = append(buffer, t)
130 | tagStarted = true
131 | }
132 | default:
133 | if tagStarted {
134 | buffer = append(buffer, t)
135 | } else {
136 | words = append(words, t)
137 | }
138 | }
139 | }
140 | if len(buffer) != 0 {
141 | if tagStarted {
142 | query.Tags = append(query.Tags, buffer[0])
143 | words = append(words, buffer[1:]...)
144 | } else {
145 | words = append(words, buffer...)
146 | }
147 | }
148 |
149 | query.LastToken = query.Tokens[len(query.Tokens)-1]
150 | query.WordString = strings.TrimSpace(strings.Join(words, " "))
151 |
152 | return query
153 | }
154 |
155 | func Autocomplete(litedb db.LiteDB, query Query) (bool, error) {
156 | autocompleted, err := AutocompleteTags(litedb, query)
157 | if err != nil {
158 | return false, err
159 | }
160 | if autocompleted {
161 | return autocompleted, nil
162 | }
163 |
164 | return AutocompleteSpecial(litedb, query)
165 | }
166 |
167 | func AutocompleteSpecial(litedb db.LiteDB, query Query) (bool, error) {
168 | if strings.HasPrefix(query.LastToken, "@") {
169 | for _, s := range special {
170 | if strings.HasPrefix(s, query.LastToken) {
171 | autocomplete := strings.Join(query.Tokens[:len(query.Tokens)-1], " ") + " " + s + " "
172 | alfred.Add(alfred.Item{
173 | Title: s,
174 | Autocomplete: strings.TrimLeft(autocomplete, " "),
175 | Valid: alfred.Bool(false),
176 | UID: s,
177 | })
178 | }
179 | }
180 | return true, nil
181 | }
182 |
183 | if strings.HasPrefix(query.LastToken, "-@") {
184 | for _, s := range special {
185 | if strings.HasPrefix(s, query.LastToken[1:]) {
186 | s = "-" + s
187 | autocomplete := strings.Join(query.Tokens[:len(query.Tokens)-1], " ") + " " + s + " "
188 | alfred.Add(alfred.Item{
189 | Title: s,
190 | Autocomplete: strings.TrimLeft(autocomplete, " "),
191 | Valid: alfred.Bool(false),
192 | UID: s,
193 | })
194 | }
195 | }
196 | return true, nil
197 | }
198 |
199 | return false, nil
200 | }
201 |
202 | func AutocompleteTags(litedb db.LiteDB, query Query) (bool, error) {
203 | if strings.HasPrefix(query.LastToken, "#") {
204 | rows, err := litedb.Query(fmt.Sprintf(db.TAGS_BY_TITLE, db.RemoveTagHashes(query.LastToken)))
205 | if err != nil {
206 | return false, err
207 | }
208 |
209 | for _, row := range rows {
210 | tag := "#" + row[db.TitleKey]
211 | if strings.Contains(tag, " ") {
212 | tag += "#"
213 | }
214 | autocomplete := strings.Join(query.Tokens[:len(query.Tokens)-1], " ") + " " + tag + " "
215 | alfred.Add(alfred.Item{
216 | Title: tag,
217 | Autocomplete: strings.TrimLeft(autocomplete, " "),
218 | Valid: alfred.Bool(false),
219 | UID: tag,
220 | })
221 | }
222 | return true, nil
223 | }
224 | return false, nil
225 | }
226 |
227 | func escape(s string) string {
228 | return strings.Replace(s, "'", "''", -1)
229 | }
230 |
231 | func GetSearchRows(litedb db.LiteDB, query Query) ([]db.Note, error) {
232 | switch {
233 | case query.WordString == "" && len(query.Tags) == 0 && query.LastToken == "":
234 | rows, err := litedb.Query(db.RECENT_NOTES)
235 | if err != nil {
236 | return nil, errors.WithStack(err)
237 | }
238 | return rows, nil
239 |
240 | case len(query.Tags) != 0:
241 | rows, err := litedb.QueryNotesByTextAndTags(query.WordString, query.Tags)
242 | if err != nil {
243 | return nil, errors.WithStack(err)
244 | }
245 | return rows, nil
246 |
247 | default:
248 | rows, err := litedb.QueryNotesByText(query.WordString)
249 | if err != nil {
250 | return nil, errors.WithStack(err)
251 | }
252 | return rows, nil
253 | }
254 | }
255 |
256 | func GetCreateItem(query Query) (*alfred.Item, error) {
257 | callback := []string{}
258 | if query.WordString != "" {
259 | callback = append(callback, "title="+url.PathEscape(query.WordString))
260 | }
261 | if len(query.Tags) != 0 {
262 | bareTags := []string{}
263 | for _, t := range query.Tags {
264 | bareTags = append(bareTags, url.PathEscape(db.RemoveTagHashes(t)))
265 | }
266 | callback = append(callback, "tags="+strings.Join(bareTags, ","))
267 | }
268 |
269 | clipString, err := clipboard.ReadAll()
270 | if err != nil {
271 | return nil, err
272 | }
273 | if clipString != "" {
274 | callback = append(callback, "text="+url.PathEscape(clipString))
275 | }
276 | callbackString := strings.Join(callback, "&")
277 |
278 | title := fmt.Sprintf("Create %q", query.WordString)
279 | if strings.Contains(title, `\"`) {
280 | title = fmt.Sprintf("Create '%s'", query.WordString)
281 | }
282 | item := alfred.Item{
283 | Title: title,
284 | Arg: callbackString,
285 | Valid: alfred.Bool(true),
286 | }
287 | if len(query.Tags) != 0 {
288 | item.Subtitle = strings.Join(query.Tags, " ")
289 | }
290 | return &item, nil
291 | }
292 |
293 | func getSearchCallbackString(query Query) string {
294 | callback := []string{}
295 |
296 | if query.WordString != "" {
297 | callback = append(callback, "term="+url.PathEscape(query.WordString))
298 | }
299 |
300 | if len(query.Tags) != 0 {
301 | callback = append(callback, "tag="+db.RemoveTagHashes(query.Tags[0]))
302 | }
303 |
304 | return strings.Join(callback, "&")
305 | }
306 |
307 | func GetAppSearchItem(query Query) (*alfred.Item, error) {
308 | title := "Search in Bear App"
309 | if query.WordString != "" {
310 | title = fmt.Sprintf("Search %#v in Bear App", query.WordString)
311 | }
312 |
313 | callbackString := getSearchCallbackString(query)
314 |
315 | item := alfred.Item{
316 | Title: title,
317 | Arg: callbackString,
318 | Valid: alfred.Bool(true),
319 | }
320 | if len(query.Tags) != 0 {
321 | item.Subtitle = query.Tags[0]
322 | }
323 | return &item, nil
324 | }
325 |
--------------------------------------------------------------------------------
/core/core_test.go:
--------------------------------------------------------------------------------
1 | package core_test
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/smartystreets/assertions"
7 |
8 | "github.com/drgrib/alfred-bear/core"
9 | )
10 |
11 | func TestParseQuery(t *testing.T) {
12 | tests := []struct {
13 | name string
14 | arg string
15 | expected core.Query
16 | }{
17 | {
18 | name: "empty arg",
19 | arg: "",
20 | expected: core.Query{Tokens: []string{""}},
21 | },
22 | {
23 | name: "single word",
24 | arg: "hello",
25 | expected: core.Query{
26 | Tokens: []string{"hello"},
27 | LastToken: "hello",
28 | WordString: "hello",
29 | },
30 | },
31 | {
32 | name: "two words",
33 | arg: "hello world",
34 | expected: core.Query{
35 | Tokens: []string{"hello", "world"},
36 | LastToken: "world",
37 | WordString: "hello world",
38 | },
39 | },
40 | {
41 | name: "multiple words with bad spacing",
42 | arg: "hello \t world",
43 | expected: core.Query{
44 | Tokens: []string{"hello", "world"},
45 | LastToken: "world",
46 | WordString: "hello world",
47 | },
48 | },
49 | {
50 | name: "argument contains tag",
51 | arg: "hello #inbox stuff",
52 | expected: core.Query{
53 | Tokens: []string{"hello", "#inbox", "stuff"},
54 | Tags: []string{"#inbox"},
55 | LastToken: "stuff",
56 | WordString: "hello stuff",
57 | },
58 | },
59 | {
60 | name: "tag is the last token",
61 | arg: "hello #inbox",
62 | expected: core.Query{
63 | Tokens: []string{"hello", "#inbox"},
64 | Tags: []string{"#inbox"},
65 | LastToken: "#inbox",
66 | WordString: "hello",
67 | },
68 | },
69 | {
70 | name: "multiword tag",
71 | arg: "oh boy #hello tag#",
72 | expected: core.Query{
73 | Tokens: []string{"oh", "boy", "#hello", "tag#"},
74 | Tags: []string{"#hello tag#"},
75 | LastToken: "tag#",
76 | WordString: "oh boy",
77 | },
78 | },
79 | {
80 | name: "multiword tag with later text",
81 | arg: "oh boy #hello tag# more text",
82 | expected: core.Query{
83 | Tokens: []string{"oh", "boy", "#hello", "tag#", "more", "text"},
84 | Tags: []string{"#hello tag#"},
85 | LastToken: "text",
86 | WordString: "oh boy more text",
87 | },
88 | },
89 | }
90 |
91 | for _, test := range tests {
92 | // nolint: scopelint
93 | t.Run(test.name, func(t *testing.T) {
94 | ok, msg := assertions.So(
95 | core.ParseQuery(test.arg),
96 | assertions.ShouldResemble,
97 | test.expected,
98 | )
99 | if !ok {
100 | t.Error(msg)
101 | }
102 | })
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/db/db.go:
--------------------------------------------------------------------------------
1 | package db
2 |
3 | import (
4 | "bytes"
5 | "database/sql"
6 | "fmt"
7 | "os/user"
8 | "path/filepath"
9 | "regexp"
10 | "sort"
11 | "strings"
12 | "text/template"
13 |
14 | "github.com/mattn/go-sqlite3"
15 | "github.com/pkg/errors"
16 | )
17 |
18 | const (
19 | DbPath = "~/Library/Group Containers/9K33E3U3T4.net.shinyfrog.bear/Application Data/database.sqlite"
20 |
21 | TitleKey = "ZTITLE"
22 | TextKey = "ZTEXT"
23 | TagsKey = "TAGS"
24 | NoteIDKey = "ZUNIQUEIDENTIFIER"
25 |
26 | RECENT_NOTES = `
27 | SELECT
28 | note.ZUNIQUEIDENTIFIER,
29 | note.ZTITLE,
30 | GROUP_CONCAT(tag.ZTITLE) AS TAGS
31 | FROM
32 | ZSFNOTE note
33 | LEFT JOIN
34 | Z_5TAGS nTag ON note.Z_PK = nTag.Z_5NOTES
35 | LEFT JOIN
36 | ZSFNOTETAG tag ON nTag.Z_13TAGS = tag.Z_PK
37 | WHERE
38 | note.ZARCHIVED = 0
39 | AND note.ZTRASHED = 0
40 | GROUP BY
41 | note.ZUNIQUEIDENTIFIER,
42 | note.ZTITLE
43 | ORDER BY
44 | note.ZMODIFICATIONDATE DESC
45 | LIMIT 25
46 | `
47 |
48 | NOTES_BY_QUERY = `
49 | SELECT
50 | note.ZUNIQUEIDENTIFIER,
51 | note.ZTITLE,
52 | GROUP_CONCAT(tag.ZTITLE) AS TAGS
53 | FROM
54 | ZSFNOTE note
55 | LEFT JOIN
56 | Z_5TAGS nTag ON note.Z_PK = nTag.Z_5NOTES
57 | LEFT JOIN
58 | ZSFNOTETAG tag ON nTag.Z_13TAGS = tag.Z_PK
59 | WHERE
60 | note.ZUNIQUEIDENTIFIER IN (
61 | SELECT
62 | note.ZUNIQUEIDENTIFIER
63 | FROM
64 | ZSFNOTE note
65 | LEFT JOIN
66 | ZSFNOTEFILE images ON images.ZNOTE = note.Z_PK
67 | WHERE
68 | note.ZARCHIVED = 0
69 | AND note.ZTRASHED = 0
70 | AND note.ZTEXT IS NOT NULL
71 | AND (
72 | utflower(note.ZTITLE) LIKE utflower('%'||$1||'%') OR
73 | utflower(note.ZTEXT) LIKE utflower('%'||$1||'%') OR
74 | images.ZSEARCHTEXT LIKE utflower('%'||$1||'%')
75 | )
76 | )
77 | GROUP BY
78 | note.ZUNIQUEIDENTIFIER,
79 | note.ZTITLE
80 | ORDER BY
81 | CASE WHEN utflower(note.ZTITLE) LIKE utflower('%'||$1||'%') THEN 0 ELSE 1 END,
82 | note.ZMODIFICATIONDATE DESC
83 | LIMIT 200
84 | `
85 |
86 | NOTES_BY_TAGS_AND_QUERY = `
87 | WITH
88 | joined AS (
89 | SELECT
90 | note.ZUNIQUEIDENTIFIER,
91 | note.ZTITLE,
92 | note.ZTEXT,
93 | note.ZMODIFICATIONDATE,
94 | tag.ZTITLE AS TAG_TITLE,
95 | images.ZSEARCHTEXT
96 | FROM
97 | ZSFNOTE note
98 | INNER JOIN
99 | Z_5TAGS nTag ON note.Z_PK = nTag.Z_5NOTES
100 | INNER JOIN
101 | ZSFNOTETAG tag ON nTag.Z_13TAGS = tag.Z_PK
102 | LEFT JOIN
103 | ZSFNOTEFILE images ON images.ZNOTE = note.Z_PK
104 | WHERE
105 | note.ZARCHIVED = 0
106 | AND note.ZTRASHED = 0
107 | AND note.ZTEXT IS NOT NULL
108 | ),
109 | hasSearchedTags AS (
110 | {{ .TagIntersection}}
111 | )
112 | SELECT
113 | ZUNIQUEIDENTIFIER,
114 | ZTITLE,
115 | GROUP_CONCAT(DISTINCT TAG_TITLE) AS TAGS
116 | FROM
117 | joined
118 | WHERE
119 | ZUNIQUEIDENTIFIER IN hasSearchedTags
120 | AND (
121 | utflower(ZTITLE) LIKE utflower('%{{ .Text}}%') OR
122 | utflower(ZTEXT) LIKE utflower('%{{ .Text}}%') OR
123 | ZSEARCHTEXT LIKE utflower('%{{ .Text}}%')
124 | )
125 | GROUP BY
126 | ZUNIQUEIDENTIFIER,
127 | ZTITLE
128 | ORDER BY
129 | CASE WHEN utflower(ZTITLE) LIKE utflower('%{{ .Text}}%') THEN 0 ELSE 1 END,
130 | ZMODIFICATIONDATE DESC
131 | LIMIT 200
132 | `
133 |
134 | TAGS_BY_TITLE = `
135 | SELECT
136 | DISTINCT t.ZTITLE
137 | FROM
138 | ZSFNOTE n
139 | INNER JOIN
140 | Z_5TAGS nt ON n.Z_PK = nt.Z_5NOTES
141 | INNER JOIN
142 | ZSFNOTETAG t ON nt.Z_13TAGS = t.Z_PK
143 | WHERE
144 | n.ZARCHIVED = 0
145 | AND n.ZTRASHED = 0
146 | AND UTFLOWER(t.ZTITLE) LIKE UTFLOWER('%%%s%%')
147 | ORDER BY
148 | t.ZMODIFICATIONDATE DESC
149 | LIMIT 25
150 |
151 | `
152 |
153 | NOTE_TITLE_BY_ID = `
154 | SELECT
155 | DISTINCT ZTITLE
156 | FROM
157 | ZSFNOTE
158 | WHERE
159 | ZARCHIVED = 0
160 | AND ZTRASHED = 0
161 | AND ZUNIQUEIDENTIFIER = '%s'
162 | ORDER BY
163 | ZMODIFICATIONDATE DESC
164 | LIMIT 25
165 | `
166 | NOTE_TEXT_BY_ID = `
167 | SELECT
168 | DISTINCT ZTEXT
169 | FROM
170 | ZSFNOTE
171 | WHERE
172 | ZARCHIVED = 0
173 | AND ZTRASHED = 0
174 | AND ZUNIQUEIDENTIFIER = '%s'
175 | ORDER BY
176 | ZMODIFICATIONDATE DESC
177 | LIMIT 25
178 | `
179 | )
180 |
181 | type TagQueryArg struct {
182 | Text string
183 | TagIntersection string
184 | }
185 |
186 | func TemplateToString(templateStr string, data any) (string, error) {
187 | var buffer bytes.Buffer
188 | t := template.Must(template.New("").Parse(templateStr))
189 | err := t.Execute(&buffer, data)
190 | return buffer.String(), errors.WithStack(err)
191 | }
192 |
193 | type Note map[string]string
194 |
195 | func Expanduser(path string) string {
196 | usr, _ := user.Current()
197 | dir := usr.HomeDir
198 | if path[:2] == "~/" {
199 | path = filepath.Join(dir, path[2:])
200 | }
201 | return path
202 | }
203 |
204 | type LiteDB struct {
205 | db *sql.DB
206 | }
207 |
208 | func NewLiteDB(path string) (LiteDB, error) {
209 | sql.Register("sqlite3_custom", &sqlite3.SQLiteDriver{
210 | ConnectHook: func(conn *sqlite3.SQLiteConn) error {
211 | return conn.RegisterFunc("utflower", utfLower, true)
212 | },
213 | })
214 |
215 | db, err := sql.Open("sqlite3_custom", path)
216 | litedb := LiteDB{db}
217 | return litedb, err
218 | }
219 |
220 | func utfLower(s string) string {
221 | return strings.ToLower(s)
222 | }
223 |
224 | func NewBearDB() (LiteDB, error) {
225 | path := Expanduser(DbPath)
226 | litedb, err := NewLiteDB(path)
227 | return litedb, err
228 | }
229 |
230 | func (litedb LiteDB) Query(q string, args ...interface{}) ([]Note, error) {
231 | results := []Note{}
232 | rows, err := litedb.db.Query(q, args...)
233 | if err != nil {
234 | return results, errors.WithStack(err)
235 | }
236 |
237 | cols, err := rows.Columns()
238 | if err != nil {
239 | return results, errors.WithStack(err)
240 | }
241 |
242 | for rows.Next() {
243 | m := Note{}
244 | columns := make([]interface{}, len(cols))
245 | columnPointers := make([]interface{}, len(cols))
246 | for i := range columns {
247 | columnPointers[i] = &columns[i]
248 | }
249 | if err := rows.Scan(columnPointers...); err != nil {
250 | return results, errors.WithStack(err)
251 | }
252 | for i, colName := range cols {
253 | val := columnPointers[i].(*interface{})
254 | s, ok := (*val).(string)
255 | if ok {
256 | m[colName] = s
257 | } else {
258 | m[colName] = ""
259 | }
260 | }
261 | results = append(results, m)
262 | }
263 | err = rows.Close()
264 | if err != nil {
265 | return results, errors.WithStack(err)
266 | }
267 | err = rows.Err()
268 | if err != nil {
269 | return results, errors.WithStack(err)
270 | }
271 | return results, errors.WithStack(err)
272 | }
273 |
274 | func escape(s string) string {
275 | return strings.Replace(s, "'", "''", -1)
276 | }
277 |
278 | func containsOrderedWords(text string, words []string) bool {
279 | prev := 0
280 | for _, w := range words {
281 | i := strings.Index(text, w)
282 | if i == -1 || i < prev {
283 | return false
284 | }
285 | prev = i
286 | }
287 | return true
288 | }
289 |
290 | func containsWords(text string, words []string) bool {
291 | for _, w := range words {
292 | if !strings.Contains(text, w) {
293 | return false
294 | }
295 | }
296 | return true
297 | }
298 |
299 | func (litedb LiteDB) queryNotesByTextAndTagConjunction(text, tagIntersection string, tags []string) ([]Note, error) {
300 | tagQueryArg := TagQueryArg{
301 | Text: escape(text),
302 | TagIntersection: tagIntersection,
303 | }
304 |
305 | query, err := TemplateToString(NOTES_BY_TAGS_AND_QUERY, tagQueryArg)
306 | if err != nil {
307 | return nil, err
308 | }
309 |
310 | return litedb.Query(query)
311 | }
312 |
313 | func RemoveTagHashes(tag string) string {
314 | tag = tag[1:]
315 | if strings.HasSuffix(tag, "#") {
316 | tag = tag[:len(tag)-1]
317 | }
318 | return tag
319 | }
320 |
321 | func (litedb LiteDB) QueryNotesByTextAndTags(text string, tags []string) ([]Note, error) {
322 | var selectStatements []string
323 | for _, t := range tags {
324 | s := fmt.Sprintf("SELECT ZUNIQUEIDENTIFIER FROM joined WHERE utflower(TAG_TITLE) = utflower('%s')", RemoveTagHashes(t))
325 | selectStatements = append(selectStatements, s)
326 | }
327 | tagIntersection := strings.Join(selectStatements, "\nINTERSECT\n")
328 |
329 | wordQuery := func(word string) ([]Note, error) {
330 | return litedb.queryNotesByTextAndTagConjunction(word, tagIntersection, tags)
331 | }
332 |
333 | return multiWordQuery(text, wordQuery)
334 | }
335 |
336 | func (litedb LiteDB) QueryNotesByText(text string) ([]Note, error) {
337 | wordQuery := func(word string) ([]Note, error) {
338 | word = escape(word)
339 | return litedb.Query(NOTES_BY_QUERY, word)
340 | }
341 | return multiWordQuery(text, wordQuery)
342 | }
343 |
344 | func splitSpacesOrQuoted(s string) []string {
345 | r := regexp.MustCompile(`([^\s"']+)|"([^"]*)"`)
346 | matches := r.FindAllStringSubmatch(s, -1)
347 | var split []string
348 | for _, v := range matches {
349 | match := v[1]
350 | if match == "" {
351 | match = v[2]
352 | }
353 | split = append(split, match)
354 | }
355 | return split
356 | }
357 |
358 | type noteRecord struct {
359 | note Note
360 | contains bool
361 | containsOrderedWords bool
362 | containsWords bool
363 | originalIndex int
364 | }
365 |
366 | func NewNoteRecord(i int, note Note, lowerText string) *noteRecord {
367 | title := strings.ToLower(note[TitleKey])
368 | words := strings.Split(lowerText, " ")
369 | record := noteRecord{
370 | originalIndex: i,
371 | note: note,
372 | contains: strings.Contains(title, lowerText),
373 | containsOrderedWords: containsOrderedWords(title, words),
374 | containsWords: containsWords(title, words),
375 | }
376 | return &record
377 | }
378 |
379 | func multiWordQuery(text string, wordQuery func(string) ([]Note, error)) ([]Note, error) {
380 | lowerText := strings.ToLower(text)
381 | words := splitSpacesOrQuoted(lowerText)
382 |
383 | var records []*noteRecord
384 | fullMatch := map[string]bool{}
385 | notes, err := wordQuery(lowerText)
386 | if err != nil {
387 | return nil, err
388 | }
389 | for i, note := range notes {
390 | noteId := note[NoteIDKey]
391 | record := NewNoteRecord(i, note, lowerText)
392 | record.originalIndex = i
393 | records = append(records, record)
394 | fullMatch[noteId] = true
395 | }
396 |
397 | var multiRecords []*noteRecord
398 | count := map[string]int{}
399 | for _, word := range words {
400 | notes, err := wordQuery(word)
401 | if err != nil {
402 | return nil, err
403 | }
404 |
405 | for i, note := range notes {
406 | noteId := note[NoteIDKey]
407 | if count[noteId] == 0 && !fullMatch[noteId] {
408 | record := NewNoteRecord(i, note, lowerText)
409 | record.originalIndex = i
410 | multiRecords = append(multiRecords, record)
411 | }
412 | count[noteId]++
413 | }
414 | }
415 |
416 | for _, record := range multiRecords {
417 | if count[record.note[NoteIDKey]] == len(words) || record.containsWords {
418 | records = append(records, record)
419 | }
420 | }
421 |
422 | sort.Slice(records, func(i, j int) bool {
423 | iRecord := records[i]
424 | jRecord := records[j]
425 |
426 | if iRecord.contains != jRecord.contains {
427 | return iRecord.contains
428 | }
429 |
430 | if iRecord.containsOrderedWords != jRecord.containsOrderedWords {
431 | return iRecord.containsOrderedWords
432 | }
433 |
434 | if iRecord.containsWords != jRecord.containsWords {
435 | return iRecord.containsWords
436 | }
437 |
438 | return iRecord.originalIndex < jRecord.originalIndex
439 | })
440 |
441 | var finalRows []Note
442 | for _, noteRecord := range records {
443 | finalRows = append(finalRows, noteRecord.note)
444 | }
445 |
446 | return finalRows, nil
447 | }
448 |
--------------------------------------------------------------------------------
/db/db_test.go:
--------------------------------------------------------------------------------
1 | package db_test
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/smartystreets/assertions"
7 |
8 | "github.com/drgrib/alfred-bear/db"
9 | )
10 |
11 | func TestRemoveTagHashes(t *testing.T) {
12 | tests := []struct {
13 | tag string
14 | expected string
15 | }{
16 | {
17 | tag: "#simple",
18 | expected: "simple",
19 | },
20 | {
21 | tag: "#multi word#",
22 | expected: "multi word",
23 | },
24 | {
25 | tag: "#multi word long#",
26 | expected: "multi word long",
27 | },
28 | }
29 |
30 | for _, test := range tests {
31 | // nolint: scopelint
32 | t.Run(test.tag, func(t *testing.T) {
33 | ok, msg := assertions.So(
34 | db.RemoveTagHashes(test.tag),
35 | assertions.ShouldResemble,
36 | test.expected,
37 | )
38 | if !ok {
39 | t.Error(msg)
40 | }
41 | })
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/doc/Authorize.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drgrib/alfred-bear/10590fdeefc57ad502aeed4d51cb53e8ed648708/doc/Authorize.png
--------------------------------------------------------------------------------
/doc/BasicSearch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drgrib/alfred-bear/10590fdeefc57ad502aeed4d51cb53e8ed648708/doc/BasicSearch.png
--------------------------------------------------------------------------------
/doc/CreateSearch1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drgrib/alfred-bear/10590fdeefc57ad502aeed4d51cb53e8ed648708/doc/CreateSearch1.png
--------------------------------------------------------------------------------
/doc/CreateSearch2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drgrib/alfred-bear/10590fdeefc57ad502aeed4d51cb53e8ed648708/doc/CreateSearch2.png
--------------------------------------------------------------------------------
/doc/Link1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drgrib/alfred-bear/10590fdeefc57ad502aeed4d51cb53e8ed648708/doc/Link1.png
--------------------------------------------------------------------------------
/doc/Link2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drgrib/alfred-bear/10590fdeefc57ad502aeed4d51cb53e8ed648708/doc/Link2.png
--------------------------------------------------------------------------------
/doc/NewNote.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drgrib/alfred-bear/10590fdeefc57ad502aeed4d51cb53e8ed648708/doc/NewNote.png
--------------------------------------------------------------------------------
/doc/RecentNotes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drgrib/alfred-bear/10590fdeefc57ad502aeed4d51cb53e8ed648708/doc/RecentNotes.png
--------------------------------------------------------------------------------
/doc/SearchQueryInBearApp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drgrib/alfred-bear/10590fdeefc57ad502aeed4d51cb53e8ed648708/doc/SearchQueryInBearApp.png
--------------------------------------------------------------------------------
/doc/SearchSpecialInBearApp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drgrib/alfred-bear/10590fdeefc57ad502aeed4d51cb53e8ed648708/doc/SearchSpecialInBearApp.png
--------------------------------------------------------------------------------
/doc/SpecialSearchAutocomplete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drgrib/alfred-bear/10590fdeefc57ad502aeed4d51cb53e8ed648708/doc/SpecialSearchAutocomplete.png
--------------------------------------------------------------------------------
/doc/TagAnyOrder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drgrib/alfred-bear/10590fdeefc57ad502aeed4d51cb53e8ed648708/doc/TagAnyOrder.png
--------------------------------------------------------------------------------
/doc/TagAutocomplete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drgrib/alfred-bear/10590fdeefc57ad502aeed4d51cb53e8ed648708/doc/TagAutocomplete.png
--------------------------------------------------------------------------------
/doc/TagAutocompleteMultiple.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drgrib/alfred-bear/10590fdeefc57ad502aeed4d51cb53e8ed648708/doc/TagAutocompleteMultiple.png
--------------------------------------------------------------------------------
/doc/TagAutocompleteSearch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drgrib/alfred-bear/10590fdeefc57ad502aeed4d51cb53e8ed648708/doc/TagAutocompleteSearch.png
--------------------------------------------------------------------------------
/doc/TagFilter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drgrib/alfred-bear/10590fdeefc57ad502aeed4d51cb53e8ed648708/doc/TagFilter.png
--------------------------------------------------------------------------------
/doc/TagTextSearch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drgrib/alfred-bear/10590fdeefc57ad502aeed4d51cb53e8ed648708/doc/TagTextSearch.png
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/drgrib/alfred-bear
2 |
3 | go 1.21
4 |
5 | require (
6 | github.com/atotto/clipboard v0.1.2
7 | github.com/drgrib/alfred v0.0.0-20180713231015-cbcb1a4b1a30
8 | github.com/drgrib/mac v0.0.0-20200321221020-4f366006daac
9 | github.com/mattn/go-sqlite3 v1.14.9
10 | github.com/pkg/errors v0.9.1
11 | github.com/smartystreets/assertions v1.1.1
12 | golang.org/x/text v0.3.8
13 | )
14 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/atotto/clipboard v0.1.2 h1:YZCtFu5Ie8qX2VmVTBnrqLSiU9XOWwqNRmdT3gIQzbY=
2 | github.com/atotto/clipboard v0.1.2/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
3 | github.com/drgrib/alfred v0.0.0-20180713231015-cbcb1a4b1a30 h1:YJztq9iEgAjAxoyya/tNRcI3MHH3oZIu6cdwUOSCSAc=
4 | github.com/drgrib/alfred v0.0.0-20180713231015-cbcb1a4b1a30/go.mod h1:i0gA+4g42sioTtf5J8DHLunASnD4RhyzXQqVNeoTV/o=
5 | github.com/drgrib/mac v0.0.0-20200321221020-4f366006daac h1:dbnAsr/ngd8DSzd3x/2CGQ8ubL5oX2R+Cwdin66Snbc=
6 | github.com/drgrib/mac v0.0.0-20200321221020-4f366006daac/go.mod h1:UaS3hPRZNCmKPJ1c9eKlIaehAOpSpBDLEsJTP/U6BSs=
7 | github.com/mattn/go-sqlite3 v1.14.9 h1:10HX2Td0ocZpYEjhilsuo6WWtUqttj2Kb0KtD86/KYA=
8 | github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
9 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
10 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
11 | github.com/smartystreets/assertions v1.1.1 h1:T/YLemO5Yp7KPzS+lVtu+WsHn8yoSwTfItdAd1r3cck=
12 | github.com/smartystreets/assertions v1.1.1/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
13 | golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
14 | golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
15 |
--------------------------------------------------------------------------------
/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drgrib/alfred-bear/10590fdeefc57ad502aeed4d51cb53e8ed648708/icon.png
--------------------------------------------------------------------------------
/info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | bundleid
6 | com.drgrib.bear
7 | category
8 | Tools
9 | connections
10 |
11 | 00E5D1C8-D8AE-43EF-9E0D-92F25F59FE62
12 |
13 |
14 | destinationuid
15 | E92972B8-8164-49C7-BBA8-064C3B42EC29
16 | modifiers
17 | 0
18 | modifiersubtext
19 |
20 | vitoclose
21 |
22 |
23 |
24 | 0344727C-3BE1-4BC7-887A-82BECFC34B13
25 |
26 |
27 | destinationuid
28 | EFC3FC25-36AC-43BA-8054-038DDAD82831
29 | modifiers
30 | 0
31 | modifiersubtext
32 |
33 | vitoclose
34 |
35 |
36 |
37 | 11295A3D-C960-41A3-9670-DFCBDF58C9D9
38 |
39 |
40 | destinationuid
41 | E2FCB7C2-A812-4419-9CD0-2E74282DF3FE
42 | modifiers
43 | 524288
44 | modifiersubtext
45 | Search Query in Bear App
46 | vitoclose
47 |
48 |
49 |
50 | destinationuid
51 | 6CEE5F7C-4415-417F-A095-4CE906185A93
52 | modifiers
53 | 0
54 | modifiersubtext
55 |
56 | vitoclose
57 |
58 |
59 |
60 | destinationuid
61 | C09F10FC-E57F-47DA-9C55-84E9A68D2348
62 | modifiers
63 | 1048576
64 | modifiersubtext
65 | Open Note in Bear App
66 | vitoclose
67 |
68 |
69 |
70 | destinationuid
71 | 905FC2FC-858B-476F-9DBA-D143CD446DFC
72 | modifiers
73 | 131072
74 | modifiersubtext
75 | Paste Wiki-Link to Note
76 | vitoclose
77 |
78 |
79 |
80 | destinationuid
81 | D3FAABE3-B891-4CF6-BD99-89994FAC5A36
82 | modifiers
83 | 1179648
84 | modifiersubtext
85 | Paste Link to Note
86 | vitoclose
87 |
88 |
89 |
90 | destinationuid
91 | 1E42F790-0DFA-452B-A0F5-5FEA0ACA22E5
92 | modifiers
93 | 1572864
94 | modifiersubtext
95 | Paste Table of Contents
96 | vitoclose
97 |
98 |
99 |
100 | 1197E934-E010-48A8-AF8E-CF0CF301C261
101 |
102 |
103 | destinationuid
104 | 5EC53F91-D330-4140-B05A-96D7DCFCD135
105 | modifiers
106 | 0
107 | modifiersubtext
108 |
109 | vitoclose
110 |
111 |
112 |
113 | 1E42F790-0DFA-452B-A0F5-5FEA0ACA22E5
114 |
115 |
116 | destinationuid
117 | A17A4C24-15DE-4D34-9704-76E6483B412C
118 | modifiers
119 | 0
120 | modifiersubtext
121 |
122 | vitoclose
123 |
124 |
125 |
126 | 23C93BAC-491B-42FF-8E63-F529CFCBCCD7
127 |
128 |
129 | destinationuid
130 | 00E5D1C8-D8AE-43EF-9E0D-92F25F59FE62
131 | modifiers
132 | 0
133 | modifiersubtext
134 |
135 | vitoclose
136 |
137 |
138 |
139 | 48FBADDF-A8D0-41D7-B180-469BA359C57F
140 |
141 |
142 | destinationuid
143 | 1E89EC6B-5E60-4AEE-800A-55A1E91B9182
144 | modifiers
145 | 1048576
146 | modifiersubtext
147 | Paste Clipboard Contents
148 | vitoclose
149 |
150 |
151 |
152 | destinationuid
153 | F652448D-56D0-4068-AAA0-9670D6771371
154 | modifiers
155 | 0
156 | modifiersubtext
157 |
158 | vitoclose
159 |
160 |
161 |
162 | 4D2DFA01-3886-42C0-82EE-5B0BE63B02D2
163 |
164 |
165 | destinationuid
166 | 4D5617AA-ADE7-45B5-9C05-DA6C69781662
167 | modifiers
168 | 0
169 | modifiersubtext
170 |
171 | vitoclose
172 |
173 |
174 |
175 | 4D5617AA-ADE7-45B5-9C05-DA6C69781662
176 |
177 |
178 | destinationuid
179 | E2FCB7C2-A812-4419-9CD0-2E74282DF3FE
180 | modifiers
181 | 524288
182 | modifiersubtext
183 | Search Query in Bear App
184 | vitoclose
185 |
186 |
187 |
188 | destinationuid
189 | 6CEE5F7C-4415-417F-A095-4CE906185A93
190 | modifiers
191 | 0
192 | modifiersubtext
193 |
194 | vitoclose
195 |
196 |
197 |
198 | destinationuid
199 | C09F10FC-E57F-47DA-9C55-84E9A68D2348
200 | modifiers
201 | 1048576
202 | modifiersubtext
203 | Open Note in Bear App
204 | vitoclose
205 |
206 |
207 |
208 | destinationuid
209 | 905FC2FC-858B-476F-9DBA-D143CD446DFC
210 | modifiers
211 | 131072
212 | modifiersubtext
213 | Paste Wiki-Link to Note
214 | vitoclose
215 |
216 |
217 |
218 | destinationuid
219 | D3FAABE3-B891-4CF6-BD99-89994FAC5A36
220 | modifiers
221 | 1179648
222 | modifiersubtext
223 | Paste Link to Note
224 | vitoclose
225 |
226 |
227 |
228 | destinationuid
229 | 1E42F790-0DFA-452B-A0F5-5FEA0ACA22E5
230 | modifiers
231 | 1572864
232 | modifiersubtext
233 | Paste Table of Contents
234 | vitoclose
235 |
236 |
237 |
238 | 59A4C675-2898-4D74-AA9C-C3F3E4A96A0A
239 |
240 |
241 | destinationuid
242 | E2FCB7C2-A812-4419-9CD0-2E74282DF3FE
243 | modifiers
244 | 524288
245 | modifiersubtext
246 | Search Query in Bear App
247 | vitoclose
248 |
249 |
250 |
251 | destinationuid
252 | 6CEE5F7C-4415-417F-A095-4CE906185A93
253 | modifiers
254 | 0
255 | modifiersubtext
256 |
257 | vitoclose
258 |
259 |
260 |
261 | destinationuid
262 | C09F10FC-E57F-47DA-9C55-84E9A68D2348
263 | modifiers
264 | 1048576
265 | modifiersubtext
266 | Open Note in Bear App
267 | vitoclose
268 |
269 |
270 |
271 | destinationuid
272 | 905FC2FC-858B-476F-9DBA-D143CD446DFC
273 | modifiers
274 | 131072
275 | modifiersubtext
276 | Paste Wiki-Link to Note
277 | vitoclose
278 |
279 |
280 |
281 | destinationuid
282 | D3FAABE3-B891-4CF6-BD99-89994FAC5A36
283 | modifiers
284 | 1179648
285 | modifiersubtext
286 | Paste Link to Note
287 | vitoclose
288 |
289 |
290 |
291 | destinationuid
292 | 1E42F790-0DFA-452B-A0F5-5FEA0ACA22E5
293 | modifiers
294 | 1572864
295 | modifiersubtext
296 | Paste Table of Contents
297 | vitoclose
298 |
299 |
300 |
301 | 5D8E2482-13A9-4FCB-96CA-C66F35949E2A
302 |
303 |
304 | destinationuid
305 | E2FCB7C2-A812-4419-9CD0-2E74282DF3FE
306 | modifiers
307 | 524288
308 | modifiersubtext
309 | Search Query in Bear App
310 | vitoclose
311 |
312 |
313 |
314 | destinationuid
315 | 6CEE5F7C-4415-417F-A095-4CE906185A93
316 | modifiers
317 | 0
318 | modifiersubtext
319 |
320 | vitoclose
321 |
322 |
323 |
324 | destinationuid
325 | C09F10FC-E57F-47DA-9C55-84E9A68D2348
326 | modifiers
327 | 1048576
328 | modifiersubtext
329 | Open Note in Bear App
330 | vitoclose
331 |
332 |
333 |
334 | destinationuid
335 | 905FC2FC-858B-476F-9DBA-D143CD446DFC
336 | modifiers
337 | 131072
338 | modifiersubtext
339 | Paste Wiki-Link to Note
340 | vitoclose
341 |
342 |
343 |
344 | destinationuid
345 | D3FAABE3-B891-4CF6-BD99-89994FAC5A36
346 | modifiers
347 | 1179648
348 | modifiersubtext
349 | Paste Link to Note
350 | vitoclose
351 |
352 |
353 |
354 | destinationuid
355 | 1E42F790-0DFA-452B-A0F5-5FEA0ACA22E5
356 | modifiers
357 | 1572864
358 | modifiersubtext
359 | Paste Table of Contents
360 | vitoclose
361 |
362 |
363 |
364 | 5EC53F91-D330-4140-B05A-96D7DCFCD135
365 |
366 |
367 | destinationuid
368 | 27456FAE-DBC0-4A91-9753-517B00831DB3
369 | modifiers
370 | 0
371 | modifiersubtext
372 |
373 | vitoclose
374 |
375 |
376 |
377 | 6CEE5F7C-4415-417F-A095-4CE906185A93
378 |
379 |
380 | destinationuid
381 | F652448D-56D0-4068-AAA0-9670D6771371
382 | modifiers
383 | 0
384 | modifiersubtext
385 |
386 | sourceoutputuid
387 | 296ECF8E-6E4D-49DA-9B92-9883C5F5605A
388 | vitoclose
389 |
390 |
391 |
392 | destinationuid
393 | EFC3FC25-36AC-43BA-8054-038DDAD82831
394 | modifiers
395 | 0
396 | modifiersubtext
397 |
398 | sourceoutputuid
399 | FDEE12A9-8486-48CB-B48E-6893D573BBD7
400 | vitoclose
401 |
402 |
403 |
404 | destinationuid
405 | EE68D5DB-1C41-45BB-94EF-267F95F56024
406 | modifiers
407 | 0
408 | modifiersubtext
409 |
410 | vitoclose
411 |
412 |
413 |
414 | 905FC2FC-858B-476F-9DBA-D143CD446DFC
415 |
416 |
417 | destinationuid
418 | 23C93BAC-491B-42FF-8E63-F529CFCBCCD7
419 | modifiers
420 | 0
421 | modifiersubtext
422 |
423 | vitoclose
424 |
425 |
426 |
427 | 954F3962-1F0B-42A0-9C03-127E2690FA49
428 |
429 |
430 | destinationuid
431 | 9E833A18-9A79-45A4-808C-D1891E6147CE
432 | modifiers
433 | 0
434 | modifiersubtext
435 |
436 | vitoclose
437 |
438 |
439 |
440 | 9E833A18-9A79-45A4-808C-D1891E6147CE
441 |
442 |
443 | destinationuid
444 | 7936E4EC-9DD4-4C26-BFFD-9A300B4DC3D4
445 | modifiers
446 | 0
447 | modifiersubtext
448 |
449 | vitoclose
450 |
451 |
452 |
453 | A17A4C24-15DE-4D34-9704-76E6483B412C
454 |
455 |
456 | destinationuid
457 | B5A664D0-6B7A-4F14-9967-A9D90AA32295
458 | modifiers
459 | 0
460 | modifiersubtext
461 |
462 | vitoclose
463 |
464 |
465 |
466 | A6BFE29A-BCCF-476F-8E07-A7C9CA647422
467 |
468 |
469 | destinationuid
470 | 2B3D525C-64BF-4146-92F4-A7012F05F6A2
471 | modifiers
472 | 0
473 | modifiersubtext
474 |
475 | vitoclose
476 |
477 |
478 |
479 | B39E12A9-4E49-45C4-AE46-F56DEBD40B39
480 |
481 |
482 | destinationuid
483 | B0876656-9842-4ED2-A972-BD955ED9E68A
484 | modifiers
485 | 0
486 | modifiersubtext
487 |
488 | vitoclose
489 |
490 |
491 |
492 | B5A664D0-6B7A-4F14-9967-A9D90AA32295
493 |
494 |
495 | destinationuid
496 | DA7313D4-6521-4C7B-B408-903CF0C760FB
497 | modifiers
498 | 0
499 | modifiersubtext
500 |
501 | vitoclose
502 |
503 |
504 |
505 | C09F10FC-E57F-47DA-9C55-84E9A68D2348
506 |
507 |
508 | destinationuid
509 | B39E12A9-4E49-45C4-AE46-F56DEBD40B39
510 | modifiers
511 | 0
512 | modifiersubtext
513 |
514 | sourceoutputuid
515 | 296ECF8E-6E4D-49DA-9B92-9883C5F5605A
516 | vitoclose
517 |
518 |
519 |
520 | destinationuid
521 | EFC3FC25-36AC-43BA-8054-038DDAD82831
522 | modifiers
523 | 0
524 | modifiersubtext
525 |
526 | sourceoutputuid
527 | FDEE12A9-8486-48CB-B48E-6893D573BBD7
528 | vitoclose
529 |
530 |
531 |
532 | destinationuid
533 | 954F3962-1F0B-42A0-9C03-127E2690FA49
534 | modifiers
535 | 0
536 | modifiersubtext
537 |
538 | vitoclose
539 |
540 |
541 |
542 | C3AB4283-78DC-43E8-A8E5-BAB521E3E48D
543 |
544 |
545 | destinationuid
546 | 1E89EC6B-5E60-4AEE-800A-55A1E91B9182
547 | modifiers
548 | 1048576
549 | modifiersubtext
550 | Paste Clipboard Contents
551 | vitoclose
552 |
553 |
554 |
555 | destinationuid
556 | F652448D-56D0-4068-AAA0-9670D6771371
557 | modifiers
558 | 0
559 | modifiersubtext
560 |
561 | vitoclose
562 |
563 |
564 |
565 | D3FAABE3-B891-4CF6-BD99-89994FAC5A36
566 |
567 |
568 | destinationuid
569 | 1197E934-E010-48A8-AF8E-CF0CF301C261
570 | modifiers
571 | 0
572 | modifiersubtext
573 |
574 | vitoclose
575 |
576 |
577 |
578 | E2FCB7C2-A812-4419-9CD0-2E74282DF3FE
579 |
580 |
581 | destinationuid
582 | B39E12A9-4E49-45C4-AE46-F56DEBD40B39
583 | modifiers
584 | 0
585 | modifiersubtext
586 |
587 | sourceoutputuid
588 | 296ECF8E-6E4D-49DA-9B92-9883C5F5605A
589 | vitoclose
590 |
591 |
592 |
593 | destinationuid
594 | 0344727C-3BE1-4BC7-887A-82BECFC34B13
595 | modifiers
596 | 0
597 | modifiersubtext
598 |
599 | vitoclose
600 |
601 |
602 |
603 | destinationuid
604 | EFC3FC25-36AC-43BA-8054-038DDAD82831
605 | modifiers
606 | 0
607 | modifiersubtext
608 |
609 | sourceoutputuid
610 | FDEE12A9-8486-48CB-B48E-6893D573BBD7
611 | vitoclose
612 |
613 |
614 |
615 | EE68D5DB-1C41-45BB-94EF-267F95F56024
616 |
617 |
618 | destinationuid
619 | A6BFE29A-BCCF-476F-8E07-A7C9CA647422
620 | modifiers
621 | 0
622 | modifiersubtext
623 |
624 | vitoclose
625 |
626 |
627 |
628 | F652448D-56D0-4068-AAA0-9670D6771371
629 |
630 |
631 | destinationuid
632 | 1E89EC6B-5E60-4AEE-800A-55A1E91B9182
633 | modifiers
634 | 0
635 | modifiersubtext
636 |
637 | vitoclose
638 |
639 |
640 |
641 | FED949AC-9EA3-45BB-90ED-8309D3BFB323
642 |
643 |
644 | destinationuid
645 | 5D8E2482-13A9-4FCB-96CA-C66F35949E2A
646 | modifiers
647 | 0
648 | modifiersubtext
649 |
650 | vitoclose
651 |
652 |
653 |
654 |
655 | createdby
656 | drgrib
657 | description
658 | Streamlined note searching and creation for Bear using Alfred
659 | disabled
660 |
661 | name
662 | Bear
663 | objects
664 |
665 |
666 | config
667 |
668 | alfredfiltersresults
669 |
670 | alfredfiltersresultsmatchmode
671 | 0
672 | argumenttreatemptyqueryasnil
673 |
674 | argumenttrimmode
675 | 0
676 | argumenttype
677 | 0
678 | escaping
679 | 102
680 | keyword
681 | bn
682 | queuedelaycustom
683 | 3
684 | queuedelayimmediatelyinitially
685 |
686 | queuedelaymode
687 | 0
688 | queuemode
689 | 1
690 | runningsubtext
691 | Creating...
692 | script
693 | cmd/create/create "{query}"
694 | scriptargtype
695 | 0
696 | scriptfile
697 |
698 | subtext
699 |
700 | title
701 | Create New Bear Note
702 | type
703 | 0
704 | withspace
705 |
706 |
707 | type
708 | alfred.workflow.input.scriptfilter
709 | uid
710 | 48FBADDF-A8D0-41D7-B180-469BA359C57F
711 | version
712 | 3
713 |
714 |
715 | config
716 |
717 | concurrently
718 |
719 | escaping
720 | 102
721 | script
722 | open "bear://x-callback-url/create?{query}&show_window=yes&new_window=yes&edit=yes"
723 | scriptargtype
724 | 0
725 | scriptfile
726 |
727 | type
728 | 0
729 |
730 | type
731 | alfred.workflow.action.script
732 | uid
733 | 1E89EC6B-5E60-4AEE-800A-55A1E91B9182
734 | version
735 | 2
736 |
737 |
738 | config
739 |
740 | action
741 | 0
742 | argument
743 | 0
744 | focusedappvariable
745 |
746 | focusedappvariablename
747 |
748 | hotkey
749 | 0
750 | hotmod
751 | 0
752 | hotstring
753 |
754 | leftcursor
755 |
756 | modsmode
757 | 0
758 | relatedAppsMode
759 | 0
760 |
761 | type
762 | alfred.workflow.trigger.hotkey
763 | uid
764 | FED949AC-9EA3-45BB-90ED-8309D3BFB323
765 | version
766 | 2
767 |
768 |
769 | config
770 |
771 | alfredfiltersresults
772 |
773 | alfredfiltersresultsmatchmode
774 | 0
775 | argumenttreatemptyqueryasnil
776 |
777 | argumenttrimmode
778 | 0
779 | argumenttype
780 | 0
781 | escaping
782 | 102
783 | keyword
784 | bnew
785 | queuedelaycustom
786 | 3
787 | queuedelayimmediatelyinitially
788 |
789 | queuedelaymode
790 | 0
791 | queuemode
792 | 1
793 | runningsubtext
794 | Creating...
795 | script
796 | cmd/create/create "{query}"
797 | scriptargtype
798 | 0
799 | scriptfile
800 |
801 | subtext
802 |
803 | title
804 | Create New Bear Note
805 | type
806 | 0
807 | withspace
808 |
809 |
810 | type
811 | alfred.workflow.input.scriptfilter
812 | uid
813 | C3AB4283-78DC-43E8-A8E5-BAB521E3E48D
814 | version
815 | 3
816 |
817 |
818 | config
819 |
820 | concurrently
821 |
822 | escaping
823 | 102
824 | script
825 | open "bear://x-callback-url/create?{query}&show_window=yes&new_window=no&edit=yes"
826 | scriptargtype
827 | 0
828 | scriptfile
829 |
830 | type
831 | 0
832 |
833 | type
834 | alfred.workflow.action.script
835 | uid
836 | B0876656-9842-4ED2-A972-BD955ED9E68A
837 | version
838 | 2
839 |
840 |
841 | config
842 |
843 | matchmode
844 | 1
845 | matchstring
846 | &text=.+
847 | regexcaseinsensitive
848 |
849 | regexmultiline
850 |
851 | replacestring
852 |
853 |
854 | type
855 | alfred.workflow.utility.replace
856 | uid
857 | F652448D-56D0-4068-AAA0-9670D6771371
858 | version
859 | 2
860 |
861 |
862 | config
863 |
864 | alfredfiltersresults
865 |
866 | alfredfiltersresultsmatchmode
867 | 0
868 | argumenttreatemptyqueryasnil
869 |
870 | argumenttrimmode
871 | 0
872 | argumenttype
873 | 1
874 | escaping
875 | 102
876 | keyword
877 | bcs
878 | queuedelaycustom
879 | 3
880 | queuedelayimmediatelyinitially
881 |
882 | queuedelaymode
883 | 0
884 | queuemode
885 | 1
886 | runningsubtext
887 | Searching...
888 | script
889 | cmd/csearch/csearch "$1"
890 | scriptargtype
891 | 1
892 | scriptfile
893 |
894 | subtext
895 |
896 | title
897 | Create/Search Bear Notes
898 | type
899 | 0
900 | withspace
901 |
902 |
903 | type
904 | alfred.workflow.input.scriptfilter
905 | uid
906 | 5D8E2482-13A9-4FCB-96CA-C66F35949E2A
907 | version
908 | 3
909 |
910 |
911 | config
912 |
913 | conditions
914 |
915 |
916 | inputstring
917 |
918 | matchcasesensitive
919 |
920 | matchmode
921 | 4
922 | matchstring
923 | ^(title=|tags=|text=)
924 | outputlabel
925 | CreateItem
926 | uid
927 | 296ECF8E-6E4D-49DA-9B92-9883C5F5605A
928 |
929 |
930 | inputstring
931 |
932 | matchcasesensitive
933 |
934 | matchmode
935 | 4
936 | matchstring
937 | ^(term=|tag=)|^$
938 | outputlabel
939 | AppSearchItem
940 | uid
941 | FDEE12A9-8486-48CB-B48E-6893D573BBD7
942 |
943 |
944 | elselabel
945 | NoteID|searchCallback
946 | hideelse
947 |
948 |
949 | type
950 | alfred.workflow.utility.conditional
951 | uid
952 | E2FCB7C2-A812-4419-9CD0-2E74282DF3FE
953 | version
954 | 1
955 |
956 |
957 | config
958 |
959 | matchmode
960 | 1
961 | matchstring
962 | &text=.+
963 | regexcaseinsensitive
964 |
965 | regexmultiline
966 |
967 | replacestring
968 |
969 |
970 | type
971 | alfred.workflow.utility.replace
972 | uid
973 | B39E12A9-4E49-45C4-AE46-F56DEBD40B39
974 | version
975 | 2
976 |
977 |
978 | config
979 |
980 | matchmode
981 | 1
982 | matchstring
983 | ([^\|]+)\|([^\|]*)
984 | regexcaseinsensitive
985 |
986 | regexmultiline
987 |
988 | replacestring
989 | $2
990 |
991 | type
992 | alfred.workflow.utility.replace
993 | uid
994 | 0344727C-3BE1-4BC7-887A-82BECFC34B13
995 | version
996 | 2
997 |
998 |
999 | config
1000 |
1001 | concurrently
1002 |
1003 | escaping
1004 | 102
1005 | script
1006 | open "bear://x-callback-url/search?{query}"
1007 | scriptargtype
1008 | 0
1009 | scriptfile
1010 |
1011 | type
1012 | 0
1013 |
1014 | type
1015 | alfred.workflow.action.script
1016 | uid
1017 | EFC3FC25-36AC-43BA-8054-038DDAD82831
1018 | version
1019 | 2
1020 |
1021 |
1022 | config
1023 |
1024 | alfredfiltersresults
1025 |
1026 | alfredfiltersresultsmatchmode
1027 | 0
1028 | argumenttreatemptyqueryasnil
1029 |
1030 | argumenttrimmode
1031 | 0
1032 | argumenttype
1033 | 1
1034 | escaping
1035 | 102
1036 | keyword
1037 | bcsearch
1038 | queuedelaycustom
1039 | 3
1040 | queuedelayimmediatelyinitially
1041 |
1042 | queuedelaymode
1043 | 0
1044 | queuemode
1045 | 1
1046 | runningsubtext
1047 | Searching...
1048 | script
1049 | cmd/csearch/csearch "{query}"
1050 | scriptargtype
1051 | 0
1052 | scriptfile
1053 |
1054 | subtext
1055 |
1056 | title
1057 | Create/Search Bear Notes
1058 | type
1059 | 0
1060 | withspace
1061 |
1062 |
1063 | type
1064 | alfred.workflow.input.scriptfilter
1065 | uid
1066 | 59A4C675-2898-4D74-AA9C-C3F3E4A96A0A
1067 | version
1068 | 3
1069 |
1070 |
1071 | config
1072 |
1073 | conditions
1074 |
1075 |
1076 | inputstring
1077 |
1078 | matchcasesensitive
1079 |
1080 | matchmode
1081 | 4
1082 | matchstring
1083 | ^(title=|tags=|text=)
1084 | outputlabel
1085 | CreateItem
1086 | uid
1087 | 296ECF8E-6E4D-49DA-9B92-9883C5F5605A
1088 |
1089 |
1090 | inputstring
1091 |
1092 | matchcasesensitive
1093 |
1094 | matchmode
1095 | 4
1096 | matchstring
1097 | ^(term=|tag=)|^$
1098 | outputlabel
1099 | AppSearchItem
1100 | uid
1101 | FDEE12A9-8486-48CB-B48E-6893D573BBD7
1102 |
1103 |
1104 | elselabel
1105 | NoteID|searchCallback
1106 | hideelse
1107 |
1108 |
1109 | type
1110 | alfred.workflow.utility.conditional
1111 | uid
1112 | 6CEE5F7C-4415-417F-A095-4CE906185A93
1113 | version
1114 | 1
1115 |
1116 |
1117 | config
1118 |
1119 | conditions
1120 |
1121 |
1122 | inputstring
1123 |
1124 | matchcasesensitive
1125 |
1126 | matchmode
1127 | 4
1128 | matchstring
1129 | ^(title=|tags=|text=)
1130 | outputlabel
1131 | CreateItem
1132 | uid
1133 | 296ECF8E-6E4D-49DA-9B92-9883C5F5605A
1134 |
1135 |
1136 | inputstring
1137 |
1138 | matchcasesensitive
1139 |
1140 | matchmode
1141 | 4
1142 | matchstring
1143 | ^(term=|tag=)|^$
1144 | outputlabel
1145 | AppSearchItem
1146 | uid
1147 | FDEE12A9-8486-48CB-B48E-6893D573BBD7
1148 |
1149 |
1150 | elselabel
1151 | NoteID|searchCallback
1152 | hideelse
1153 |
1154 |
1155 | type
1156 | alfred.workflow.utility.conditional
1157 | uid
1158 | C09F10FC-E57F-47DA-9C55-84E9A68D2348
1159 | version
1160 | 1
1161 |
1162 |
1163 | config
1164 |
1165 | concurrently
1166 |
1167 | escaping
1168 | 102
1169 | script
1170 | cmd/setcursor/setcursor
1171 | scriptargtype
1172 | 1
1173 | scriptfile
1174 |
1175 | type
1176 | 0
1177 |
1178 | type
1179 | alfred.workflow.action.script
1180 | uid
1181 | 2B3D525C-64BF-4146-92F4-A7012F05F6A2
1182 | version
1183 | 2
1184 |
1185 |
1186 | config
1187 |
1188 | concurrently
1189 |
1190 | escaping
1191 | 102
1192 | script
1193 | open "bear://x-callback-url/open-note?id={query}&show_window=yes&new_window=yes&edit=yes"
1194 | scriptargtype
1195 | 0
1196 | scriptfile
1197 |
1198 | type
1199 | 0
1200 |
1201 | type
1202 | alfred.workflow.action.script
1203 | uid
1204 | A6BFE29A-BCCF-476F-8E07-A7C9CA647422
1205 | version
1206 | 2
1207 |
1208 |
1209 | config
1210 |
1211 | matchmode
1212 | 1
1213 | matchstring
1214 | ([^\|]+)\|([^\|]*)
1215 | regexcaseinsensitive
1216 |
1217 | regexmultiline
1218 |
1219 | replacestring
1220 | $1
1221 |
1222 | type
1223 | alfred.workflow.utility.replace
1224 | uid
1225 | EE68D5DB-1C41-45BB-94EF-267F95F56024
1226 | version
1227 | 2
1228 |
1229 |
1230 | config
1231 |
1232 | concurrently
1233 |
1234 | escaping
1235 | 102
1236 | script
1237 | open "bear://x-callback-url/open-note?id={query}&show_window=yes&new_window=no&edit=yes"
1238 | scriptargtype
1239 | 0
1240 | scriptfile
1241 |
1242 | type
1243 | 0
1244 |
1245 | type
1246 | alfred.workflow.action.script
1247 | uid
1248 | 9E833A18-9A79-45A4-808C-D1891E6147CE
1249 | version
1250 | 2
1251 |
1252 |
1253 | config
1254 |
1255 | concurrently
1256 |
1257 | escaping
1258 | 102
1259 | script
1260 | cmd/setcursor/setcursor
1261 | scriptargtype
1262 | 1
1263 | scriptfile
1264 |
1265 | type
1266 | 0
1267 |
1268 | type
1269 | alfred.workflow.action.script
1270 | uid
1271 | 7936E4EC-9DD4-4C26-BFFD-9A300B4DC3D4
1272 | version
1273 | 2
1274 |
1275 |
1276 | config
1277 |
1278 | matchmode
1279 | 1
1280 | matchstring
1281 | ([^\|]+)\|([^\|]*)
1282 | regexcaseinsensitive
1283 |
1284 | regexmultiline
1285 |
1286 | replacestring
1287 | $1
1288 |
1289 | type
1290 | alfred.workflow.utility.replace
1291 | uid
1292 | 954F3962-1F0B-42A0-9C03-127E2690FA49
1293 | version
1294 | 2
1295 |
1296 |
1297 | config
1298 |
1299 | alfredfiltersresults
1300 |
1301 | alfredfiltersresultsmatchmode
1302 | 0
1303 | argumenttreatemptyqueryasnil
1304 |
1305 | argumenttrimmode
1306 | 0
1307 | argumenttype
1308 | 1
1309 | escaping
1310 | 102
1311 | keyword
1312 | bs
1313 | queuedelaycustom
1314 | 3
1315 | queuedelayimmediatelyinitially
1316 |
1317 | queuedelaymode
1318 | 0
1319 | queuemode
1320 | 1
1321 | runningsubtext
1322 | Searching...
1323 | script
1324 | cmd/search/search "$1"
1325 | scriptargtype
1326 | 1
1327 | scriptfile
1328 |
1329 | subtext
1330 |
1331 | title
1332 | Search Bear Notes
1333 | type
1334 | 0
1335 | withspace
1336 |
1337 |
1338 | type
1339 | alfred.workflow.input.scriptfilter
1340 | uid
1341 | 11295A3D-C960-41A3-9670-DFCBDF58C9D9
1342 | version
1343 | 3
1344 |
1345 |
1346 | config
1347 |
1348 | availableviaurlhandler
1349 |
1350 | triggerid
1351 | search bear
1352 |
1353 | type
1354 | alfred.workflow.trigger.external
1355 | uid
1356 | 4D2DFA01-3886-42C0-82EE-5B0BE63B02D2
1357 | version
1358 | 1
1359 |
1360 |
1361 | config
1362 |
1363 | alfredfiltersresults
1364 |
1365 | alfredfiltersresultsmatchmode
1366 | 0
1367 | argumenttreatemptyqueryasnil
1368 |
1369 | argumenttrimmode
1370 | 0
1371 | argumenttype
1372 | 1
1373 | escaping
1374 | 102
1375 | keyword
1376 | bsearch
1377 | queuedelaycustom
1378 | 3
1379 | queuedelayimmediatelyinitially
1380 |
1381 | queuedelaymode
1382 | 0
1383 | queuemode
1384 | 1
1385 | runningsubtext
1386 | Searching...
1387 | script
1388 | cmd/search/search "{query}"
1389 | scriptargtype
1390 | 0
1391 | scriptfile
1392 |
1393 | subtext
1394 |
1395 | title
1396 | Search Bear Notes
1397 | type
1398 | 0
1399 | withspace
1400 |
1401 |
1402 | type
1403 | alfred.workflow.input.scriptfilter
1404 | uid
1405 | 4D5617AA-ADE7-45B5-9C05-DA6C69781662
1406 | version
1407 | 3
1408 |
1409 |
1410 | config
1411 |
1412 | conditions
1413 |
1414 |
1415 | inputstring
1416 |
1417 | matchcasesensitive
1418 |
1419 | matchmode
1420 | 4
1421 | matchstring
1422 | ^(title=|tags=|text=)
1423 | outputlabel
1424 | CreateItem
1425 | uid
1426 | 296ECF8E-6E4D-49DA-9B92-9883C5F5605A
1427 |
1428 |
1429 | inputstring
1430 |
1431 | matchcasesensitive
1432 |
1433 | matchmode
1434 | 4
1435 | matchstring
1436 | ^(term=|tag=)|^$
1437 | outputlabel
1438 | AppSearchItem
1439 | uid
1440 | FDEE12A9-8486-48CB-B48E-6893D573BBD7
1441 |
1442 |
1443 | elselabel
1444 | NoteID|searchCallback
1445 | hideelse
1446 |
1447 |
1448 | type
1449 | alfred.workflow.utility.conditional
1450 | uid
1451 | 905FC2FC-858B-476F-9DBA-D143CD446DFC
1452 | version
1453 | 1
1454 |
1455 |
1456 | config
1457 |
1458 | autopaste
1459 |
1460 | clipboardtext
1461 | {query}
1462 | ignoredynamicplaceholders
1463 |
1464 | transient
1465 |
1466 |
1467 | type
1468 | alfred.workflow.output.clipboard
1469 | uid
1470 | E92972B8-8164-49C7-BBA8-064C3B42EC29
1471 | version
1472 | 3
1473 |
1474 |
1475 | config
1476 |
1477 | concurrently
1478 |
1479 | escaping
1480 | 102
1481 | script
1482 | cmd/wiki-link/wiki-link "{query}"
1483 | scriptargtype
1484 | 0
1485 | scriptfile
1486 |
1487 | type
1488 | 0
1489 |
1490 | type
1491 | alfred.workflow.action.script
1492 | uid
1493 | 00E5D1C8-D8AE-43EF-9E0D-92F25F59FE62
1494 | version
1495 | 2
1496 |
1497 |
1498 | config
1499 |
1500 | matchmode
1501 | 1
1502 | matchstring
1503 | ([^\|]+)\|([^\|]*)
1504 | regexcaseinsensitive
1505 |
1506 | regexmultiline
1507 |
1508 | replacestring
1509 | $1
1510 |
1511 | type
1512 | alfred.workflow.utility.replace
1513 | uid
1514 | 23C93BAC-491B-42FF-8E63-F529CFCBCCD7
1515 | version
1516 | 2
1517 |
1518 |
1519 | config
1520 |
1521 | conditions
1522 |
1523 |
1524 | inputstring
1525 |
1526 | matchcasesensitive
1527 |
1528 | matchmode
1529 | 4
1530 | matchstring
1531 | ^(title=|tags=|text=)
1532 | outputlabel
1533 | CreateItem
1534 | uid
1535 | 296ECF8E-6E4D-49DA-9B92-9883C5F5605A
1536 |
1537 |
1538 | inputstring
1539 |
1540 | matchcasesensitive
1541 |
1542 | matchmode
1543 | 4
1544 | matchstring
1545 | ^(term=|tag=)|^$
1546 | outputlabel
1547 | AppSearchItem
1548 | uid
1549 | FDEE12A9-8486-48CB-B48E-6893D573BBD7
1550 |
1551 |
1552 | elselabel
1553 | NoteID|searchCallback
1554 | hideelse
1555 |
1556 |
1557 | type
1558 | alfred.workflow.utility.conditional
1559 | uid
1560 | D3FAABE3-B891-4CF6-BD99-89994FAC5A36
1561 | version
1562 | 1
1563 |
1564 |
1565 | config
1566 |
1567 | concurrently
1568 |
1569 | escaping
1570 | 102
1571 | script
1572 | cmd/link/link "{query}"
1573 | scriptargtype
1574 | 0
1575 | scriptfile
1576 |
1577 | type
1578 | 0
1579 |
1580 | type
1581 | alfred.workflow.action.script
1582 | uid
1583 | 5EC53F91-D330-4140-B05A-96D7DCFCD135
1584 | version
1585 | 2
1586 |
1587 |
1588 | config
1589 |
1590 | autopaste
1591 |
1592 | clipboardtext
1593 | {query}
1594 | ignoredynamicplaceholders
1595 |
1596 | transient
1597 |
1598 |
1599 | type
1600 | alfred.workflow.output.clipboard
1601 | uid
1602 | 27456FAE-DBC0-4A91-9753-517B00831DB3
1603 | version
1604 | 3
1605 |
1606 |
1607 | config
1608 |
1609 | matchmode
1610 | 1
1611 | matchstring
1612 | ([^\|]+)\|([^\|]*)
1613 | regexcaseinsensitive
1614 |
1615 | regexmultiline
1616 |
1617 | replacestring
1618 | $1
1619 |
1620 | type
1621 | alfred.workflow.utility.replace
1622 | uid
1623 | 1197E934-E010-48A8-AF8E-CF0CF301C261
1624 | version
1625 | 2
1626 |
1627 |
1628 | config
1629 |
1630 | conditions
1631 |
1632 |
1633 | inputstring
1634 |
1635 | matchcasesensitive
1636 |
1637 | matchmode
1638 | 4
1639 | matchstring
1640 | ^(title=|tags=|text=)
1641 | outputlabel
1642 | CreateItem
1643 | uid
1644 | 296ECF8E-6E4D-49DA-9B92-9883C5F5605A
1645 |
1646 |
1647 | inputstring
1648 |
1649 | matchcasesensitive
1650 |
1651 | matchmode
1652 | 4
1653 | matchstring
1654 | ^(term=|tag=)|^$
1655 | outputlabel
1656 | AppSearchItem
1657 | uid
1658 | FDEE12A9-8486-48CB-B48E-6893D573BBD7
1659 |
1660 |
1661 | elselabel
1662 | NoteID|searchCallback
1663 | hideelse
1664 |
1665 |
1666 | type
1667 | alfred.workflow.utility.conditional
1668 | uid
1669 | 1E42F790-0DFA-452B-A0F5-5FEA0ACA22E5
1670 | version
1671 | 1
1672 |
1673 |
1674 | config
1675 |
1676 | concurrently
1677 |
1678 | escaping
1679 | 102
1680 | script
1681 | cmd/toc/toc "{query}"
1682 | scriptargtype
1683 | 0
1684 | scriptfile
1685 |
1686 | type
1687 | 0
1688 |
1689 | type
1690 | alfred.workflow.action.script
1691 | uid
1692 | B5A664D0-6B7A-4F14-9967-A9D90AA32295
1693 | version
1694 | 2
1695 |
1696 |
1697 | config
1698 |
1699 | autopaste
1700 |
1701 | clipboardtext
1702 | {query}
1703 | ignoredynamicplaceholders
1704 |
1705 | transient
1706 |
1707 |
1708 | type
1709 | alfred.workflow.output.clipboard
1710 | uid
1711 | DA7313D4-6521-4C7B-B408-903CF0C760FB
1712 | version
1713 | 3
1714 |
1715 |
1716 | config
1717 |
1718 | matchmode
1719 | 1
1720 | matchstring
1721 | ([^\|]+)\|([^\|]*)
1722 | regexcaseinsensitive
1723 |
1724 | regexmultiline
1725 |
1726 | replacestring
1727 | $1
1728 |
1729 | type
1730 | alfred.workflow.utility.replace
1731 | uid
1732 | A17A4C24-15DE-4D34-9704-76E6483B412C
1733 | version
1734 | 2
1735 |
1736 |
1737 | readme
1738 |
1739 | uidata
1740 |
1741 | 00E5D1C8-D8AE-43EF-9E0D-92F25F59FE62
1742 |
1743 | note
1744 | Format Link
1745 | xpos
1746 | 1180
1747 | ypos
1748 | 1050
1749 |
1750 | 0344727C-3BE1-4BC7-887A-82BECFC34B13
1751 |
1752 | xpos
1753 | 1095
1754 | ypos
1755 | 435
1756 |
1757 | 11295A3D-C960-41A3-9670-DFCBDF58C9D9
1758 |
1759 | xpos
1760 | 170
1761 | ypos
1762 | 880
1763 |
1764 | 1197E934-E010-48A8-AF8E-CF0CF301C261
1765 |
1766 | xpos
1767 | 1090
1768 | ypos
1769 | 1235
1770 |
1771 | 1E42F790-0DFA-452B-A0F5-5FEA0ACA22E5
1772 |
1773 | note
1774 | Paste Link to Table of Contents
1775 | xpos
1776 | 795
1777 | ypos
1778 | 1350
1779 |
1780 | 1E89EC6B-5E60-4AEE-800A-55A1E91B9182
1781 |
1782 | note
1783 | Create Note
1784 | xpos
1785 | 1380
1786 | ypos
1787 | 35
1788 |
1789 | 23C93BAC-491B-42FF-8E63-F529CFCBCCD7
1790 |
1791 | xpos
1792 | 1090
1793 | ypos
1794 | 1080
1795 |
1796 | 27456FAE-DBC0-4A91-9753-517B00831DB3
1797 |
1798 | note
1799 | Paste Link
1800 | xpos
1801 | 1335
1802 | ypos
1803 | 1205
1804 |
1805 | 2B3D525C-64BF-4146-92F4-A7012F05F6A2
1806 |
1807 | note
1808 | Set Cursor
1809 | xpos
1810 | 1380
1811 | ypos
1812 | 635
1813 |
1814 | 48FBADDF-A8D0-41D7-B180-469BA359C57F
1815 |
1816 | xpos
1817 | 355
1818 | ypos
1819 | 35
1820 |
1821 | 4D2DFA01-3886-42C0-82EE-5B0BE63B02D2
1822 |
1823 | xpos
1824 | 30
1825 | ypos
1826 | 1015
1827 |
1828 | 4D5617AA-ADE7-45B5-9C05-DA6C69781662
1829 |
1830 | xpos
1831 | 170
1832 | ypos
1833 | 1015
1834 |
1835 | 59A4C675-2898-4D74-AA9C-C3F3E4A96A0A
1836 |
1837 | xpos
1838 | 30
1839 | ypos
1840 | 450
1841 |
1842 | 5D8E2482-13A9-4FCB-96CA-C66F35949E2A
1843 |
1844 | xpos
1845 | 30
1846 | ypos
1847 | 315
1848 |
1849 | 5EC53F91-D330-4140-B05A-96D7DCFCD135
1850 |
1851 | note
1852 | Format Link
1853 | xpos
1854 | 1180
1855 | ypos
1856 | 1205
1857 |
1858 | 6CEE5F7C-4415-417F-A095-4CE906185A93
1859 |
1860 | note
1861 | Open Note in New Window
1862 | xpos
1863 | 810
1864 | ypos
1865 | 455
1866 |
1867 | 7936E4EC-9DD4-4C26-BFFD-9A300B4DC3D4
1868 |
1869 | note
1870 | Set Cursor
1871 | xpos
1872 | 1380
1873 | ypos
1874 | 805
1875 |
1876 | 905FC2FC-858B-476F-9DBA-D143CD446DFC
1877 |
1878 | note
1879 | Paste Wiki-Link to Note
1880 | xpos
1881 | 795
1882 | ypos
1883 | 1025
1884 |
1885 | 954F3962-1F0B-42A0-9C03-127E2690FA49
1886 |
1887 | xpos
1888 | 1105
1889 | ypos
1890 | 835
1891 |
1892 | 9E833A18-9A79-45A4-808C-D1891E6147CE
1893 |
1894 | note
1895 | Open Note by ID in App
1896 | xpos
1897 | 1190
1898 | ypos
1899 | 805
1900 |
1901 | A17A4C24-15DE-4D34-9704-76E6483B412C
1902 |
1903 | xpos
1904 | 1090
1905 | ypos
1906 | 1405
1907 |
1908 | A6BFE29A-BCCF-476F-8E07-A7C9CA647422
1909 |
1910 | note
1911 | Open Note by ID
1912 | xpos
1913 | 1190
1914 | ypos
1915 | 635
1916 |
1917 | B0876656-9842-4ED2-A972-BD955ED9E68A
1918 |
1919 | note
1920 | Create Note in App
1921 | xpos
1922 | 1380
1923 | ypos
1924 | 185
1925 |
1926 | B39E12A9-4E49-45C4-AE46-F56DEBD40B39
1927 |
1928 | xpos
1929 | 1195
1930 | ypos
1931 | 320
1932 |
1933 | B5A664D0-6B7A-4F14-9967-A9D90AA32295
1934 |
1935 | note
1936 | ToC
1937 | xpos
1938 | 1180
1939 | ypos
1940 | 1375
1941 |
1942 | C09F10FC-E57F-47DA-9C55-84E9A68D2348
1943 |
1944 | note
1945 | Open Note in Bear App
1946 | xpos
1947 | 810
1948 | ypos
1949 | 610
1950 |
1951 | C3AB4283-78DC-43E8-A8E5-BAB521E3E48D
1952 |
1953 | xpos
1954 | 355
1955 | ypos
1956 | 170
1957 |
1958 | D3FAABE3-B891-4CF6-BD99-89994FAC5A36
1959 |
1960 | note
1961 | Paste Link to Note
1962 | xpos
1963 | 795
1964 | ypos
1965 | 1180
1966 |
1967 | DA7313D4-6521-4C7B-B408-903CF0C760FB
1968 |
1969 | note
1970 | Paste Link
1971 | xpos
1972 | 1335
1973 | ypos
1974 | 1375
1975 |
1976 | E2FCB7C2-A812-4419-9CD0-2E74282DF3FE
1977 |
1978 | note
1979 | Search Query in Bear App
1980 | xpos
1981 | 810
1982 | ypos
1983 | 315
1984 |
1985 | E92972B8-8164-49C7-BBA8-064C3B42EC29
1986 |
1987 | note
1988 | Paste Link
1989 | xpos
1990 | 1335
1991 | ypos
1992 | 1050
1993 |
1994 | EE68D5DB-1C41-45BB-94EF-267F95F56024
1995 |
1996 | xpos
1997 | 1105
1998 | ypos
1999 | 665
2000 |
2001 | EFC3FC25-36AC-43BA-8054-038DDAD82831
2002 |
2003 | note
2004 | Search in App
2005 | xpos
2006 | 1380
2007 | ypos
2008 | 450
2009 |
2010 | F652448D-56D0-4068-AAA0-9670D6771371
2011 |
2012 | xpos
2013 | 1130
2014 | ypos
2015 | 200
2016 |
2017 | FED949AC-9EA3-45BB-90ED-8309D3BFB323
2018 |
2019 | xpos
2020 | 30
2021 | ypos
2022 | 160
2023 |
2024 |
2025 | userconfigurationconfig
2026 |
2027 | variablesdontexport
2028 |
2029 | version
2030 | 1.2.3
2031 | webaddress
2032 | https://github.com/drgrib/alfred-bear
2033 |
2034 |
2035 |
--------------------------------------------------------------------------------