tangled
alpha
login
or
join now
treethought.xyz
/
obsidian-atmark
8
fork
atom
AT protocol bookmarking platforms in obsidian
8
fork
atom
overview
issues
pulls
pipelines
rename
treethought
2 months ago
4ea366e9
7c3629c3
+41
-49
6 changed files
expand all
collapse all
unified
split
manifest.json
package.json
src
main.ts
settings.ts
views
cards.ts
collections.ts
+5
-6
manifest.json
···
1
1
{
2
2
-
"id": "sample-plugin",
3
3
-
"name": "Sample Plugin",
2
2
+
"id": "atmark",
3
3
+
"name": "ATmark",
4
4
"version": "1.0.0",
5
5
"minAppVersion": "0.15.0",
6
6
-
"description": "Demonstrates some of the capabilities of the Obsidian API.",
7
7
-
"author": "Obsidian",
8
8
-
"authorUrl": "https://obsidian.md",
9
9
-
"fundingUrl": "https://obsidian.md/pricing",
6
6
+
"description": "Obsidian plugin for AT Protocol bookmark platforms",
7
7
+
"author": "treethought",
8
8
+
"authorUrl": "https://github.com/treethought",
10
9
"isDesktopOnly": false
11
10
}
+2
-2
package.json
···
1
1
{
2
2
-
"name": "obsidian-sample-plugin",
2
2
+
"name": "obsidian-atmark",
3
3
"version": "1.0.0",
4
4
-
"description": "This is a sample plugin for Obsidian (https://obsidian.md)",
4
4
+
"description": "Obsidian plugin for AT Protocol bookmark platforms",
5
5
"main": "main.js",
6
6
"type": "module",
7
7
"scripts": {
+19
-29
src/main.ts
···
1
1
-
import { Notice, Plugin, WorkspaceLeaf } from "obsidian";
1
1
+
import { Editor, MarkdownView, Notice, Plugin, WorkspaceLeaf } from "obsidian";
2
2
import type { Client } from "@atcute/client";
3
3
import { DEFAULT_SETTINGS, AtProtoSettings, SettingTab } from "./settings";
4
4
import { createAuthenticatedClient, createPublicClient } from "./auth";
5
5
import { getCollections } from "./lib";
6
6
import { SembleCollectionsView, VIEW_TYPE_SEMBLE_COLLECTIONS } from "views/collections";
7
7
import { SembleCardsView, VIEW_TYPE_SEMBLE_CARDS } from "views/cards";
8
8
+
import { CreateCardModal } from "components/cardForm";
8
9
9
9
-
export default class MyPlugin extends Plugin {
10
10
+
export default class ATmarkPlugin extends Plugin {
10
11
settings: AtProtoSettings = DEFAULT_SETTINGS;
11
12
client: Client | null = null;
12
13
···
21
22
this.registerView(VIEW_TYPE_SEMBLE_CARDS, (leaf) => {
22
23
return new SembleCardsView(leaf, this);
23
24
});
25
25
+
this.addCommand({
26
26
+
id: 'semble-add-card',
27
27
+
name: 'Create Semble Card',
28
28
+
editorCheckCallback: (checking: boolean, editor: Editor, _view: MarkdownView) => {
29
29
+
const sel = editor.getSelection()
24
30
31
31
+
if (!this.settings.identifier || !this.settings.appPassword) {
32
32
+
new Notice("Please set your Bluesky credentials in the plugin settings to create new records.");
33
33
+
return false;
34
34
+
}
35
35
+
if (!checking) {
36
36
+
new CreateCardModal(this, sel).open();
37
37
+
}
38
38
+
return true;
25
39
26
26
-
this.addCommand({
27
27
-
id: "list-collections",
28
28
-
name: "List Collections",
29
29
-
callback: () => this.listCollections(),
30
30
-
});
40
40
+
},
41
41
+
})
42
42
+
31
43
32
44
this.addCommand({
33
45
id: "view-semble-collections",
···
64
76
await this.initClient();
65
77
}
66
78
67
67
-
private async listCollections() {
68
68
-
if (!this.client) return;
69
69
-
70
70
-
const repo = this.settings.identifier
71
71
-
72
72
-
try {
73
73
-
const resp = await getCollections(this.client, repo);
74
74
-
if (!resp.ok) {
75
75
-
new Notice(`Failed: ${resp.data?.error}`);
76
76
-
return;
77
77
-
}
78
78
-
if (resp.data.records.length === 0) {
79
79
-
new Notice("No collections found");
80
80
-
return;
81
81
-
}
82
82
-
console.log("Collections:", resp.data.records);
83
83
-
new Notice(`Found ${resp.data.records.length} collections`);
84
84
-
} catch (e) {
85
85
-
new Notice(`Failed: ${e}`);
86
86
-
}
87
87
-
}
88
79
89
80
async activateView(v: string) {
90
81
const { workspace } = this.app;
···
93
84
const leaves = workspace.getLeavesOfType(v);
94
85
95
86
if (leaves.length > 0) {
96
96
-
console.log("Found existing leaves:", leaves);
97
87
// A leaf with our view already exists, use that
98
88
leaf = leaves[0] as WorkspaceLeaf;
99
89
workspace.revealLeaf(leaf);
+3
-3
src/settings.ts
···
1
1
import { App, PluginSettingTab, Setting } from "obsidian";
2
2
-
import type MyPlugin from "./main";
2
2
+
import type ATmarkPlugin from "./main";
3
3
4
4
export interface AtProtoSettings {
5
5
identifier: string;
···
12
12
};
13
13
14
14
export class SettingTab extends PluginSettingTab {
15
15
-
plugin: MyPlugin;
15
15
+
plugin: ATmarkPlugin;
16
16
17
17
-
constructor(app: App, plugin: MyPlugin) {
17
17
+
constructor(app: App, plugin: ATmarkPlugin) {
18
18
super(app, plugin);
19
19
this.plugin = plugin;
20
20
}
+5
-5
src/views/cards.ts
···
1
1
import { ItemView, WorkspaceLeaf, setIcon } from "obsidian";
2
2
-
import type MyPlugin from "../main";
2
2
+
import type ATmarkPlugin from "../main";
3
3
import { getCollections, getCollectionLinks, getCards } from "../lib";
4
4
import type { Main as Card, NoteContent, UrlContent } from "../lexicons/types/network/cosmik/card";
5
5
import type { Main as CollectionLink } from "../lexicons/types/network/cosmik/collectionLink";
···
25
25
}
26
26
27
27
export class SembleCardsView extends ItemView {
28
28
-
plugin: MyPlugin;
28
28
+
plugin: ATmarkPlugin;
29
29
collectionUri: string | null = null;
30
30
collectionName: string = "All Cards";
31
31
32
32
-
constructor(leaf: WorkspaceLeaf, plugin: MyPlugin) {
32
32
+
constructor(leaf: WorkspaceLeaf, plugin: ATmarkPlugin) {
33
33
super(leaf);
34
34
this.plugin = plugin;
35
35
}
···
160
160
this.plugin.activateView(VIEW_TYPE_SEMBLE_COLLECTIONS);
161
161
});
162
162
163
163
-
const brand = nav.createEl("span", { text: "Semble", cls: "semble-brand" });
163
163
+
nav.createEl("span", { text: "Semble", cls: "semble-brand" });
164
164
165
165
header.createEl("h2", { text: this.collectionName, cls: "semble-page-title" });
166
166
···
194
194
const header = el.createEl("div", { cls: "semble-card-header" });
195
195
header.createEl("span", {
196
196
text: card.type,
197
197
-
cls: `semble-badge semble-badge-${card.type.toLowerCase()}`,
197
197
+
cls: `semble-badge semble-badge-${card.type?.toLowerCase() || "unknown"}`,
198
198
});
199
199
200
200
if (card.type === "NOTE") {
+7
-4
src/views/collections.ts
···
1
1
import { ItemView, WorkspaceLeaf, setIcon } from "obsidian";
2
2
-
import type MyPlugin from "../main";
2
2
+
import type ATmarkPlugin from "../main";
3
3
import { getCollections } from "../lib";
4
4
import type { Main as Collection } from "../lexicons/types/network/cosmik/collection";
5
5
import { SembleCardsView, VIEW_TYPE_SEMBLE_CARDS } from "./cards";
···
12
12
}
13
13
14
14
export class SembleCollectionsView extends ItemView {
15
15
-
plugin: MyPlugin;
15
15
+
plugin: ATmarkPlugin;
16
16
17
17
-
constructor(leaf: WorkspaceLeaf, plugin: MyPlugin) {
17
17
+
constructor(leaf: WorkspaceLeaf, plugin: ATmarkPlugin) {
18
18
super(leaf);
19
19
this.plugin = plugin;
20
20
}
···
51
51
container.empty();
52
52
container.addClass("semble-collections-view");
53
53
54
54
-
container.createEl("h4", { text: "Collections" });
54
54
+
const header = container.createEl("div", { cls: "semble-page-header" });
55
55
+
const nav = header.createEl("div", { cls: "semble-nav-row" });
56
56
+
nav.createEl("span", { text: "Semble", cls: "semble-brand" });
57
57
+
header.createEl("h2", { text: "Collections", cls: "semble-page-title" });
55
58
56
59
if (!this.plugin.client) {
57
60
container.createEl("p", { text: "Not connected. Configure credentials in settings." });