Various AT Protocol integrations with obsidian

Rename plugin, add feed command (#6)

* rename plugin, update readme

* add feed command

authored by

treethought and committed by
GitHub
3dca07dd e79f387e

+624 -527
+61 -19
README.md
··· 1 - # ATmark 1 + # Atmosphere 2 2 3 - Obsidian plugin for AT Protocol bookmarking platforms. 3 + Obsidian plugin for AT Protocol integrations, including bookmarking platforms and standard.site publishing. 4 4 5 + Surf the atmosphere from the coziness of your vault. 5 6 6 - ## Supported platforms 7 + ![](./bookmarks.png) 8 + ![](./standard.site.png) 7 9 8 - - **Semble** (`network.cosmik.*`) - Collections and cards 9 - - **Bookmarks** (`community.lexicon.bookmarks.*`) - Community bookmarks lexicon with tag filtering (supports kipclip tags) 10 - - **margin.at** (`at.margin.*`) - Bookmarks with collections and tags support 10 + ## Features 11 11 12 - ![](/preview.png) 13 - ![](/preview-sidebar.png) 12 + ### Bookmarking & Knowledge Networks 13 + 14 + View and manage bookmarks from AT Protocol platforms: 15 + 16 + - **Semble** (`network.cosmik.*`) - Collections and cards with notes 17 + - **margin.at** (`at.margin.*`) - Bookmarks with collections and tags 18 + - **Bookmarks** (`community.lexicon.bookmarks.*`) - Community bookmarks lexicon (supports kipclip tags) 19 + 20 + ### Publishing & Reading 21 + 22 + - **Publish documents** - Publish Obsidian notes to standard.site publications like [leaflet.pub](https://leaflet.pub) and [pckt.blog](https://pckt.blog) 23 + - **Blog feed** - Browse and clip documents from subscribed standard.site publications to your vault 24 + 14 25 15 26 ## Installation 16 27 17 - Install via [BRAT](https://github.com/TfTHacker/obsidian42-brat): 28 + Official release in Obsidian plugin directory is under review. For now, install with BRAT: 18 29 19 - 1. Install the BRAT plugin from Community Plugins 30 + 1. Install the [BRAT plugin](https://github.com/TfTHacker/obsidian42-brat) from Community Plugins 20 31 2. Open BRAT settings 21 32 3. Click "Add Beta plugin" 22 - 4. Enter the GitHub URL: `https://github.com/treethought/obsidian-atmark` 33 + 4. Enter: `https://github.com/treethought/obsidian-atmosphere` 23 34 5. Enable the plugin in Community Plugins 24 35 25 - ## Getting Started 36 + ## Setup 26 37 27 38 ### Authentication 28 39 29 - 1. Open Settings > ATmark 30 - 2. Enter your AT Protocol handle or DID 31 - 3. Create an app password in your AT Protocol client (Bluesky: Settings > Privacy and security > App passwords) 32 - 4. Enter the app password in the plugin settings 40 + 1. Open Settings > Atmosphere 41 + 2. Enter your AT Protocol handle (e.g., `user.bsky.social`) 42 + 3. Create an app password at [bsky.app/settings/app-passwords](https://bsky.app/settings/app-passwords) 43 + 4. Enter the app password 33 44 5. Save settings 34 45 35 - The plugin will automatically connect using your credentials. 46 + ## Commands 36 47 37 - ### Opening the View 48 + | Command | Description | 49 + |---------|-------------| 50 + | **Open bookmarks** | Opens the bookmarks view showing items from Semble, margin.at, and bookmarks lexicon | 51 + | **Publish document** | Publishes the active note to a standard.site publication | 38 52 39 - Open the command palette (Ctrl/Cmd + P) and search for "ATmark: Open view". The view will show your bookmarks from all supported platforms. 53 + Access commands via the command palette (Ctrl/Cmd + P) or ribbon icons. 54 + 55 + ## Settings 56 + 57 + | Setting | Description | Default | 58 + |---------|-------------|---------| 59 + | **Handle** | Your AT Protocol handle or DID (e.g., `user.bsky.social`) | - | 60 + | **App password** | App password created in your AT Protocol client | - | 61 + | **Clip directory** | Directory in your vault where clipped documents are saved | `AtmosphereClips` | 62 + 63 + ## Usage 64 + 65 + ### Browsing Bookmarks 66 + 67 + Switch between sources (Semble, Margin, Bookmarks) and filter by collections or tags. Supports adding cards to collections and creating collections collections, tags, and notes (for Semble). 68 + 69 + ### Publishing Documents 70 + 71 + Open a note and run "Publish document" command. After selecting one of your existing standard.site publication, your note will be converted to the applicable format (for leaflet or pckt) and update your note's properties with publication url, AT uris, etc. 72 + 73 + ### Reading & Clipping 74 + 75 + Opening the feed will present your standard.site publications. Click a publication to view it's published documents to either view in browser or clip to your vault. 76 + 77 + ## Supported Rich Content Formats 78 + 79 + - **Leaflet** - Rich blog posts with markdown support 80 + - **Pckt** - Lightweight blogging format 81 + 40 82 41 83 ## Network Use 42 84
bookmarks.png

This is a binary file and will not be displayed.

+1 -1
bun.lock
··· 3 3 "configVersion": 1, 4 4 "workspaces": { 5 5 "": { 6 - "name": "obsidian-atmark", 6 + "name": "obsidian-atmosphere", 7 7 "dependencies": { 8 8 "@atcute/atproto": "^3.1.10", 9 9 "@atcute/bluesky": "^3.2.15",
+3 -3
manifest.json
··· 1 1 { 2 - "id": "atmark", 3 - "name": "ATmark", 2 + "id": "atmosphere", 3 + "name": "Atmosphere", 4 4 "version": "0.1.9", 5 5 "minAppVersion": "0.15.0", 6 - "description": "View and manage AT Protocol bookmarks.", 6 + "description": "Various integrations with AT Protocol", 7 7 "author": "treethought", 8 8 "authorUrl": "https://github.com/treethought", 9 9 "isDesktopOnly": false
+2 -2
package.json
··· 1 1 { 2 - "name": "obsidian-atmark", 2 + "name": "obsidian-atmosphere", 3 3 "version": "0.1.9", 4 - "description": "View and manage AT Protocol bookmarks.", 4 + "description": "Various integrations with AT Protocol", 5 5 "main": "main.js", 6 6 "type": "module", 7 7 "scripts": {
preview-sidebar.png

This is a binary file and will not be displayed.

preview.png

This is a binary file and will not be displayed.

+6 -6
src/commands/publishDocument.ts
··· 1 1 import { Notice, TFile } from "obsidian"; 2 - import type ATmarkPlugin from "../main"; 2 + import type AtmospherePlugin from "../main"; 3 3 import { createDocument, putDocument, getPublication, markdownToLeafletContent, stripMarkdown, markdownToPcktContent, buildDocumentUrl } from "../lib"; 4 4 import { PublicationSelection, SelectPublicationModal } from "../components/selectPublicationModal"; 5 5 import { type ResourceUri, } from "@atcute/lexicons"; ··· 7 7 import { PubLeafletContent } from "@atcute/leaflet"; 8 8 import { BlogPcktContent } from "@atcute/pckt"; 9 9 10 - export async function publishFileAsDocument(plugin: ATmarkPlugin) { 10 + export async function publishFileAsDocument(plugin: AtmospherePlugin) { 11 11 const file = plugin.app.workspace.getActiveFile(); 12 12 if (!file) { 13 13 new Notice("No active file to publish."); ··· 44 44 } 45 45 46 46 async function updateFrontMatter( 47 - plugin: ATmarkPlugin, 47 + plugin: AtmospherePlugin, 48 48 file: TFile, 49 49 docUri: ResourceUri, 50 50 record: SiteStandardDocument.Main, ··· 75 75 } 76 76 77 77 78 - async function buildDocumentRecord(plugin: ATmarkPlugin, file: TFile): Promise<{ record: SiteStandardDocument.Main; docUri?: ResourceUri }> { 78 + async function buildDocumentRecord(plugin: AtmospherePlugin, file: TFile): Promise<{ record: SiteStandardDocument.Main; docUri?: ResourceUri }> { 79 79 const full = await plugin.app.vault.read(file); 80 80 81 81 let fm: Record<string, unknown> | null = null; ··· 144 144 return { record, docUri }; 145 145 }; 146 146 147 - async function selectPublication(plugin: ATmarkPlugin): Promise<PublicationSelection> { 147 + async function selectPublication(plugin: AtmospherePlugin): Promise<PublicationSelection> { 148 148 return new Promise<PublicationSelection>((resolve, reject) => { 149 149 let selected = false; 150 150 const modal = new SelectPublicationModal(plugin, (selection) => { ··· 167 167 168 168 169 169 async function createOrUpdateDocument( 170 - plugin: ATmarkPlugin, 170 + plugin: AtmospherePlugin, 171 171 doc: SiteStandardDocument.Main, 172 172 existingUri?: ResourceUri, 173 173 ) {
+22 -22
src/components/cardDetailModal.ts
··· 1 1 import { Modal, Notice, setIcon } from "obsidian"; 2 - import type ATmarkPlugin from "../main"; 2 + import type AtmospherePlugin from "../main"; 3 3 import { createNoteCard, deleteRecord } from "../lib"; 4 - import type { ATmarkItem } from "../sources/types"; 4 + import type { ATBookmarkItem } from "../sources/types"; 5 5 6 6 export class CardDetailModal extends Modal { 7 - plugin: ATmarkPlugin; 8 - item: ATmarkItem; 7 + plugin: AtmospherePlugin; 8 + item: ATBookmarkItem; 9 9 onSuccess?: () => void; 10 10 noteInput: HTMLTextAreaElement | null = null; 11 11 12 - constructor(plugin: ATmarkPlugin, item: ATmarkItem, onSuccess?: () => void) { 12 + constructor(plugin: AtmospherePlugin, item: ATBookmarkItem, onSuccess?: () => void) { 13 13 super(plugin.app); 14 14 this.plugin = plugin; 15 15 this.item = item; ··· 19 19 onOpen() { 20 20 const { contentEl } = this; 21 21 contentEl.empty(); 22 - contentEl.addClass("atmark-detail-modal"); 22 + contentEl.addClass("atmosphere-detail-modal"); 23 23 24 - const header = contentEl.createEl("div", { cls: "atmark-detail-header" }); 24 + const header = contentEl.createEl("div", { cls: "atmosphere-detail-header" }); 25 25 const source = this.item.getSource(); 26 26 header.createEl("span", { 27 27 text: source, 28 - cls: `atmark-badge atmark-badge-source atmark-badge-${source}`, 28 + cls: `atmosphere-badge atmosphere-badge-source atmosphere-badge-${source}`, 29 29 }); 30 30 31 31 this.item.renderDetail(contentEl); ··· 39 39 this.renderAddNoteForm(contentEl); 40 40 } 41 41 42 - const footer = contentEl.createEl("div", { cls: "atmark-detail-footer" }); 42 + const footer = contentEl.createEl("div", { cls: "atmosphere-detail-footer" }); 43 43 footer.createEl("span", { 44 44 text: `Created ${new Date(this.item.getCreatedAt()).toLocaleDateString()}`, 45 - cls: "atmark-detail-date", 45 + cls: "atmosphere-detail-date", 46 46 }); 47 47 } 48 48 ··· 50 50 const notes = this.item.getAttachedNotes?.(); 51 51 if (!notes || notes.length === 0) return; 52 52 53 - const notesSection = contentEl.createEl("div", { cls: "atmark-semble-detail-notes-section" }); 54 - notesSection.createEl("h3", { text: "Notes", cls: "atmark-detail-section-title" }); 53 + const notesSection = contentEl.createEl("div", { cls: "atmosphere-semble-detail-notes-section" }); 54 + notesSection.createEl("h3", { text: "Notes", cls: "atmosphere-detail-section-title" }); 55 55 56 56 for (const note of notes) { 57 - const noteEl = notesSection.createEl("div", { cls: "atmark-semble-detail-note" }); 57 + const noteEl = notesSection.createEl("div", { cls: "atmosphere-semble-detail-note" }); 58 58 59 - const noteContent = noteEl.createEl("div", { cls: "atmark-semble-detail-note-content" }); 60 - const noteIcon = noteContent.createEl("span", { cls: "atmark-semble-detail-note-icon" }); 59 + const noteContent = noteEl.createEl("div", { cls: "atmosphere-semble-detail-note-content" }); 60 + const noteIcon = noteContent.createEl("span", { cls: "atmosphere-semble-detail-note-icon" }); 61 61 setIcon(noteIcon, "message-square"); 62 - noteContent.createEl("p", { text: note.text, cls: "atmark-semble-detail-note-text" }); 62 + noteContent.createEl("p", { text: note.text, cls: "atmosphere-semble-detail-note-text" }); 63 63 64 - const deleteBtn = noteEl.createEl("button", { cls: "atmark-semble-note-delete-btn" }); 64 + const deleteBtn = noteEl.createEl("button", { cls: "atmosphere-semble-note-delete-btn" }); 65 65 setIcon(deleteBtn, "trash-2"); 66 66 deleteBtn.addEventListener("click", () => { 67 67 void this.handleDeleteNote(note.uri); ··· 70 70 } 71 71 72 72 private renderAddNoteForm(contentEl: HTMLElement) { 73 - const formSection = contentEl.createEl("div", { cls: "atmark-semble-detail-add-note" }); 74 - formSection.createEl("h3", { text: "Add a note", cls: "atmark-detail-section-title" }); 73 + const formSection = contentEl.createEl("div", { cls: "atmosphere-semble-detail-add-note" }); 74 + formSection.createEl("h3", { text: "Add a note", cls: "atmosphere-detail-section-title" }); 75 75 76 - const form = formSection.createEl("div", { cls: "atmark-semble-add-note-form" }); 76 + const form = formSection.createEl("div", { cls: "atmosphere-semble-add-note-form" }); 77 77 78 78 this.noteInput = form.createEl("textarea", { 79 - cls: "atmark-textarea atmark-semble-note-input", 79 + cls: "atmosphere-textarea atmosphere-semble-note-input", 80 80 attr: { placeholder: "Write a note about this item..." }, 81 81 }); 82 82 83 - const addBtn = form.createEl("button", { text: "Add note", cls: "atmark-btn atmark-btn-primary" }); 83 + const addBtn = form.createEl("button", { text: "Add note", cls: "atmosphere-btn atmosphere-btn-primary" }); 84 84 addBtn.addEventListener("click", () => { void this.handleAddNote(); }); 85 85 } 86 86
+63
src/components/cardForm.ts
··· 1 + // import { ok } from "@atcute/client"; 2 + // import { createNoteCard } from "lib"; 3 + // import AtmospherePlugin from "main"; 4 + // import { Modal, Notice } from "obsidian"; 5 + // 6 + // export class CreateCardModal extends Modal { 7 + // plugin: AtmospherePlugin; 8 + // content: string | undefined; 9 + // 10 + // 11 + // constructor(plugin: AtmospherePlugin, content?: string) { 12 + // super(plugin.app); 13 + // this.plugin = plugin; 14 + // this.content = content; 15 + // } 16 + // 17 + // onOpen() { 18 + // const { contentEl } = this; 19 + // contentEl.createEl("h2", { text: "Create new card" }); 20 + // 21 + // // Add form elements here 22 + // contentEl.createEl("label", { text: "Card title" }); 23 + // contentEl.createEl("input", { type: "text" }); 24 + // contentEl.createEl("br"); 25 + // 26 + // 27 + // let contentInput: HTMLTextAreaElement | null = null; 28 + // if (!this.content) { 29 + // contentEl.createEl("label", { text: "Card content" }); 30 + // contentInput = contentEl.createEl("textarea"); 31 + // contentEl.createEl("br"); 32 + // } else { 33 + // // fill textarea with this.content and make it read-only 34 + // contentInput = contentEl.createEl("textarea"); 35 + // contentInput.value = this.content; 36 + // contentInput.readOnly = true; 37 + // contentEl.createEl("br"); 38 + // } 39 + // 40 + // 41 + // 42 + // 43 + // const createButton = contentEl.createEl("button", { text: "Create card" }); 44 + // createButton.onclick = async () => { 45 + // let text = this.content; 46 + // if (contentInput) { 47 + // text = contentInput.value || this.content; 48 + // } 49 + // if (!text) { 50 + // new Notice("Please enter some text"); 51 + // return; 52 + // } 53 + // await ok(createNoteCard(this.plugin.client!, this.plugin.settings.identifier, text)); 54 + // new Notice("Card created successfully!"); 55 + // this.close(); 56 + // }; 57 + // } 58 + // 59 + // onClose() { 60 + // const { contentEl } = this; 61 + // contentEl.empty(); 62 + // } 63 + // }
+12 -12
src/components/createCollectionModal.ts
··· 1 1 import { Modal, Notice } from "obsidian"; 2 - import type ATmarkPlugin from "../main"; 2 + import type AtmospherePlugin from "../main"; 3 3 import { createCollection } from "../lib"; 4 4 5 5 export class CreateCollectionModal extends Modal { 6 - plugin: ATmarkPlugin; 6 + plugin: AtmospherePlugin; 7 7 onSuccess?: () => void; 8 8 9 - constructor(plugin: ATmarkPlugin, onSuccess?: () => void) { 9 + constructor(plugin: AtmospherePlugin, onSuccess?: () => void) { 10 10 super(plugin.app); 11 11 this.plugin = plugin; 12 12 this.onSuccess = onSuccess; ··· 15 15 onOpen() { 16 16 const { contentEl } = this; 17 17 contentEl.empty(); 18 - contentEl.addClass("atmark-modal"); 18 + contentEl.addClass("atmosphere-modal"); 19 19 20 20 contentEl.createEl("h2", { text: "New collection" }); 21 21 ··· 24 24 return; 25 25 } 26 26 27 - const form = contentEl.createEl("form", { cls: "atmark-form" }); 27 + const form = contentEl.createEl("form", { cls: "atmosphere-form" }); 28 28 29 - const nameGroup = form.createEl("div", { cls: "atmark-form-group" }); 29 + const nameGroup = form.createEl("div", { cls: "atmosphere-form-group" }); 30 30 nameGroup.createEl("label", { text: "Name", attr: { for: "collection-name" } }); 31 31 const nameInput = nameGroup.createEl("input", { 32 32 type: "text", 33 - cls: "atmark-input", 33 + cls: "atmosphere-input", 34 34 attr: { id: "collection-name", placeholder: "Collection name", required: "true" }, 35 35 }); 36 36 37 - const descGroup = form.createEl("div", { cls: "atmark-form-group" }); 37 + const descGroup = form.createEl("div", { cls: "atmosphere-form-group" }); 38 38 descGroup.createEl("label", { text: "Description", attr: { for: "collection-desc" } }); 39 39 const descInput = descGroup.createEl("textarea", { 40 - cls: "atmark-textarea", 40 + cls: "atmosphere-textarea", 41 41 attr: { id: "collection-desc", placeholder: "Optional description", rows: "3" }, 42 42 }); 43 43 44 - const actions = form.createEl("div", { cls: "atmark-modal-actions" }); 44 + const actions = form.createEl("div", { cls: "atmosphere-modal-actions" }); 45 45 46 46 const cancelBtn = actions.createEl("button", { 47 47 text: "Cancel", 48 - cls: "atmark-btn atmark-btn-secondary", 48 + cls: "atmosphere-btn atmosphere-btn-secondary", 49 49 type: "button", 50 50 }); 51 51 cancelBtn.addEventListener("click", () => this.close()); 52 52 53 53 const createBtn = actions.createEl("button", { 54 54 text: "Create", 55 - cls: "atmark-btn atmark-btn-primary", 55 + cls: "atmosphere-btn atmosphere-btn-primary", 56 56 type: "submit", 57 57 }); 58 58
+14 -14
src/components/createMarginCollectionModal.ts
··· 1 1 import { Modal, Notice } from "obsidian"; 2 - import type ATmarkPlugin from "../main"; 2 + import type AtmospherePlugin from "../main"; 3 3 import { createMarginCollection } from "../lib"; 4 4 5 5 export class CreateMarginCollectionModal extends Modal { 6 - plugin: ATmarkPlugin; 6 + plugin: AtmospherePlugin; 7 7 onSuccess?: () => void; 8 8 9 - constructor(plugin: ATmarkPlugin, onSuccess?: () => void) { 9 + constructor(plugin: AtmospherePlugin, onSuccess?: () => void) { 10 10 super(plugin.app); 11 11 this.plugin = plugin; 12 12 this.onSuccess = onSuccess; ··· 15 15 onOpen() { 16 16 const { contentEl } = this; 17 17 contentEl.empty(); 18 - contentEl.addClass("atmark-modal"); 18 + contentEl.addClass("atmosphere-modal"); 19 19 20 20 contentEl.createEl("h2", { text: "New margin collection" }); 21 21 ··· 24 24 return; 25 25 } 26 26 27 - const form = contentEl.createEl("form", { cls: "atmark-form" }); 27 + const form = contentEl.createEl("form", { cls: "atmosphere-form" }); 28 28 29 - const nameGroup = form.createEl("div", { cls: "atmark-form-group" }); 29 + const nameGroup = form.createEl("div", { cls: "atmosphere-form-group" }); 30 30 nameGroup.createEl("label", { text: "Name", attr: { for: "collection-name" } }); 31 31 const nameInput = nameGroup.createEl("input", { 32 32 type: "text", 33 - cls: "atmark-input", 33 + cls: "atmosphere-input", 34 34 attr: { id: "collection-name", placeholder: "Collection name", required: "true" }, 35 35 }); 36 36 37 - const iconGroup = form.createEl("div", { cls: "atmark-form-group" }); 37 + const iconGroup = form.createEl("div", { cls: "atmosphere-form-group" }); 38 38 iconGroup.createEl("label", { text: "Icon (optional)", attr: { for: "collection-icon" } }); 39 39 const iconInput = iconGroup.createEl("input", { 40 40 type: "text", 41 - cls: "atmark-input", 41 + cls: "atmosphere-input", 42 42 attr: { id: "collection-icon" }, 43 43 }); 44 44 45 - const descGroup = form.createEl("div", { cls: "atmark-form-group" }); 45 + const descGroup = form.createEl("div", { cls: "atmosphere-form-group" }); 46 46 descGroup.createEl("label", { text: "Description", attr: { for: "collection-desc" } }); 47 47 const descInput = descGroup.createEl("textarea", { 48 - cls: "atmark-textarea", 48 + cls: "atmosphere-textarea", 49 49 attr: { id: "collection-desc", placeholder: "Optional description", rows: "3" }, 50 50 }); 51 51 52 - const actions = form.createEl("div", { cls: "atmark-modal-actions" }); 52 + const actions = form.createEl("div", { cls: "atmosphere-modal-actions" }); 53 53 54 54 const cancelBtn = actions.createEl("button", { 55 55 text: "Cancel", 56 - cls: "atmark-btn atmark-btn-secondary", 56 + cls: "atmosphere-btn atmosphere-btn-secondary", 57 57 type: "button", 58 58 }); 59 59 cancelBtn.addEventListener("click", () => this.close()); 60 60 61 61 const createBtn = actions.createEl("button", { 62 62 text: "Create", 63 - cls: "atmark-btn atmark-btn-primary", 63 + cls: "atmosphere-btn atmosphere-btn-primary", 64 64 type: "submit", 65 65 }); 66 66
+10 -10
src/components/createTagModal.ts
··· 1 1 import { Modal, Notice } from "obsidian"; 2 - import type ATmarkPlugin from "../main"; 2 + import type AtmospherePlugin from "../main"; 3 3 import { createTag } from "../lib"; 4 4 5 5 export class CreateTagModal extends Modal { 6 - plugin: ATmarkPlugin; 6 + plugin: AtmospherePlugin; 7 7 onSuccess?: () => void; 8 8 9 - constructor(plugin: ATmarkPlugin, onSuccess?: () => void) { 9 + constructor(plugin: AtmospherePlugin, onSuccess?: () => void) { 10 10 super(plugin.app); 11 11 this.plugin = plugin; 12 12 this.onSuccess = onSuccess; ··· 15 15 onOpen() { 16 16 const { contentEl } = this; 17 17 contentEl.empty(); 18 - contentEl.addClass("atmark-modal"); 18 + contentEl.addClass("atmosphere-modal"); 19 19 20 20 contentEl.createEl("h2", { text: "New tag" }); 21 21 ··· 24 24 return; 25 25 } 26 26 27 - const form = contentEl.createEl("form", { cls: "atmark-form" }); 27 + const form = contentEl.createEl("form", { cls: "atmosphere-form" }); 28 28 29 - const tagGroup = form.createEl("div", { cls: "atmark-form-group" }); 29 + const tagGroup = form.createEl("div", { cls: "atmosphere-form-group" }); 30 30 tagGroup.createEl("label", { text: "Tag", attr: { for: "tag-value" } }); 31 31 const tagInput = tagGroup.createEl("input", { 32 32 type: "text", 33 - cls: "atmark-input", 33 + cls: "atmosphere-input", 34 34 attr: { id: "tag-value", placeholder: "Tag name", required: "true" }, 35 35 }); 36 36 37 - const actions = form.createEl("div", { cls: "atmark-modal-actions" }); 37 + const actions = form.createEl("div", { cls: "atmosphere-modal-actions" }); 38 38 39 39 const cancelBtn = actions.createEl("button", { 40 40 text: "Cancel", 41 - cls: "atmark-btn atmark-btn-secondary", 41 + cls: "atmosphere-btn atmosphere-btn-secondary", 42 42 type: "button", 43 43 }); 44 44 cancelBtn.addEventListener("click", () => this.close()); 45 45 46 46 const createBtn = actions.createEl("button", { 47 47 text: "Create", 48 - cls: "atmark-btn atmark-btn-primary", 48 + cls: "atmosphere-btn atmosphere-btn-primary", 49 49 type: "submit", 50 50 }); 51 51
+25 -25
src/components/editBookmarkModal.ts
··· 1 1 import { Modal, Notice } from "obsidian"; 2 2 import type { Record } from "@atcute/atproto/types/repo/listRecords"; 3 3 import type { Main as Bookmark } from "../lexicons/types/community/lexicon/bookmarks/bookmark"; 4 - import type ATmarkPlugin from "../main"; 4 + import type AtmospherePlugin from "../main"; 5 5 import { putRecord, deleteRecord, getBookmarks } from "../lib"; 6 6 7 7 type BookmarkRecord = Record & { value: Bookmark }; ··· 12 12 } 13 13 14 14 export class EditBookmarkModal extends Modal { 15 - plugin: ATmarkPlugin; 15 + plugin: AtmospherePlugin; 16 16 record: BookmarkRecord; 17 17 onSuccess?: () => void; 18 18 tagStates: TagState[] = []; 19 19 newTagInput: HTMLInputElement | null = null; 20 20 21 - constructor(plugin: ATmarkPlugin, record: BookmarkRecord, onSuccess?: () => void) { 21 + constructor(plugin: AtmospherePlugin, record: BookmarkRecord, onSuccess?: () => void) { 22 22 super(plugin.app); 23 23 this.plugin = plugin; 24 24 this.record = record; ··· 28 28 async onOpen() { 29 29 const { contentEl } = this; 30 30 contentEl.empty(); 31 - contentEl.addClass("atmark-modal"); 31 + contentEl.addClass("atmosphere-modal"); 32 32 33 33 contentEl.createEl("h2", { text: "Edit bookmark" }); 34 34 ··· 64 64 } catch (err) { 65 65 loading.remove(); 66 66 const message = err instanceof Error ? err.message : String(err); 67 - contentEl.createEl("p", { text: `Error: ${message}`, cls: "atmark-error" }); 67 + contentEl.createEl("p", { text: `Error: ${message}`, cls: "atmosphere-error" }); 68 68 } 69 69 } 70 70 71 71 private renderForm(contentEl: HTMLElement) { 72 - const form = contentEl.createEl("div", { cls: "atmark-form" }); 72 + const form = contentEl.createEl("div", { cls: "atmosphere-form" }); 73 73 74 - const tagsGroup = form.createEl("div", { cls: "atmark-form-group" }); 74 + const tagsGroup = form.createEl("div", { cls: "atmosphere-form-group" }); 75 75 tagsGroup.createEl("label", { text: "Tags" }); 76 76 77 - const tagsList = tagsGroup.createEl("div", { cls: "atmark-tag-list" }); 77 + const tagsList = tagsGroup.createEl("div", { cls: "atmosphere-tag-list" }); 78 78 for (const state of this.tagStates) { 79 79 this.addTagChip(tagsList, state); 80 80 } 81 81 82 - const newTagRow = tagsGroup.createEl("div", { cls: "atmark-tag-row" }); 82 + const newTagRow = tagsGroup.createEl("div", { cls: "atmosphere-tag-row" }); 83 83 this.newTagInput = newTagRow.createEl("input", { 84 84 type: "text", 85 - cls: "atmark-input", 85 + cls: "atmosphere-input", 86 86 attr: { placeholder: "Add new tag..." } 87 87 }); 88 88 const addBtn = newTagRow.createEl("button", { 89 89 text: "Add", 90 - cls: "atmark-btn atmark-btn-secondary", 90 + cls: "atmosphere-btn atmosphere-btn-secondary", 91 91 attr: { type: "button" } 92 92 }); 93 93 addBtn.addEventListener("click", () => { ··· 100 100 } 101 101 }); 102 102 103 - const actions = contentEl.createEl("div", { cls: "atmark-modal-actions" }); 103 + const actions = contentEl.createEl("div", { cls: "atmosphere-modal-actions" }); 104 104 105 105 const deleteBtn = actions.createEl("button", { 106 106 text: "Delete", 107 - cls: "atmark-btn atmark-btn-danger" 107 + cls: "atmosphere-btn atmosphere-btn-danger" 108 108 }); 109 109 deleteBtn.addEventListener("click", () => { this.confirmDelete(contentEl); }); 110 110 111 - actions.createEl("div", { cls: "atmark-spacer" }); 111 + actions.createEl("div", { cls: "atmosphere-spacer" }); 112 112 113 113 const cancelBtn = actions.createEl("button", { 114 114 text: "Cancel", 115 - cls: "atmark-btn atmark-btn-secondary" 115 + cls: "atmosphere-btn atmosphere-btn-secondary" 116 116 }); 117 117 cancelBtn.addEventListener("click", () => { this.close(); }); 118 118 119 119 const saveBtn = actions.createEl("button", { 120 120 text: "Save", 121 - cls: "atmark-btn atmark-btn-primary" 121 + cls: "atmosphere-btn atmosphere-btn-primary" 122 122 }); 123 123 saveBtn.addEventListener("click", () => { void this.saveChanges(); }); 124 124 } 125 125 126 126 private addTagChip(container: HTMLElement, state: TagState) { 127 - const item = container.createEl("label", { cls: "atmark-tag-item" }); 127 + const item = container.createEl("label", { cls: "atmosphere-tag-item" }); 128 128 const checkbox = item.createEl("input", { type: "checkbox" }); 129 129 checkbox.checked = state.isSelected; 130 130 checkbox.addEventListener("change", () => { ··· 136 136 private confirmDelete(contentEl: HTMLElement) { 137 137 contentEl.empty(); 138 138 contentEl.createEl("h2", { text: "Delete bookmark" }); 139 - contentEl.createEl("p", { text: "Delete this bookmark?", cls: "atmark-warning-text" }); 139 + contentEl.createEl("p", { text: "Delete this bookmark?", cls: "atmosphere-warning-text" }); 140 140 141 - const actions = contentEl.createEl("div", { cls: "atmark-modal-actions" }); 141 + const actions = contentEl.createEl("div", { cls: "atmosphere-modal-actions" }); 142 142 143 143 const cancelBtn = actions.createEl("button", { 144 144 text: "Cancel", 145 - cls: "atmark-btn atmark-btn-secondary" 145 + cls: "atmosphere-btn atmosphere-btn-secondary" 146 146 }); 147 147 cancelBtn.addEventListener("click", () => { 148 148 void this.onOpen(); ··· 150 150 151 151 const confirmBtn = actions.createEl("button", { 152 152 text: "Delete", 153 - cls: "atmark-btn atmark-btn-danger" 153 + cls: "atmosphere-btn atmosphere-btn-danger" 154 154 }); 155 155 confirmBtn.addEventListener("click", () => { void this.deleteBookmark(); }); 156 156 } ··· 166 166 const rkey = this.record.uri.split("/").pop(); 167 167 if (!rkey) { 168 168 contentEl.empty(); 169 - contentEl.createEl("p", { text: "Invalid bookmark uri.", cls: "atmark-error" }); 169 + contentEl.createEl("p", { text: "Invalid bookmark uri.", cls: "atmosphere-error" }); 170 170 return; 171 171 } 172 172 ··· 183 183 } catch (err) { 184 184 contentEl.empty(); 185 185 const message = err instanceof Error ? err.message : String(err); 186 - contentEl.createEl("p", { text: `Failed to delete: ${message}`, cls: "atmark-error" }); 186 + contentEl.createEl("p", { text: `Failed to delete: ${message}`, cls: "atmosphere-error" }); 187 187 } 188 188 } 189 189 ··· 205 205 const rkey = this.record.uri.split("/").pop(); 206 206 if (!rkey) { 207 207 contentEl.empty(); 208 - contentEl.createEl("p", { text: "Invalid bookmark uri.", cls: "atmark-error" }); 208 + contentEl.createEl("p", { text: "Invalid bookmark uri.", cls: "atmosphere-error" }); 209 209 return; 210 210 } 211 211 ··· 228 228 } catch (err) { 229 229 contentEl.empty(); 230 230 const message = err instanceof Error ? err.message : String(err); 231 - contentEl.createEl("p", { text: `Failed to save: ${message}`, cls: "atmark-error" }); 231 + contentEl.createEl("p", { text: `Failed to save: ${message}`, cls: "atmosphere-error" }); 232 232 } 233 233 } 234 234
+26 -26
src/components/editCardModal.ts
··· 1 1 import { Modal, Notice } from "obsidian"; 2 - import type ATmarkPlugin from "../main"; 2 + import type AtmospherePlugin from "../main"; 3 3 import { getCollections, getCollectionLinks, createCollectionLink, getRecord, deleteRecord } from "../lib"; 4 4 import type { Main as Collection } from "../lexicons/types/network/cosmik/collection"; 5 5 import type { Main as CollectionLink } from "../lexicons/types/network/cosmik/collectionLink"; ··· 23 23 } 24 24 25 25 export class EditCardModal extends Modal { 26 - plugin: ATmarkPlugin; 26 + plugin: AtmospherePlugin; 27 27 cardUri: string; 28 28 cardCid: string; 29 29 onSuccess?: () => void; 30 30 collectionStates: CollectionState[] = []; 31 31 32 - constructor(plugin: ATmarkPlugin, cardUri: string, cardCid: string, onSuccess?: () => void) { 32 + constructor(plugin: AtmospherePlugin, cardUri: string, cardCid: string, onSuccess?: () => void) { 33 33 super(plugin.app); 34 34 this.plugin = plugin; 35 35 this.cardUri = cardUri; ··· 40 40 async onOpen() { 41 41 const { contentEl } = this; 42 42 contentEl.empty(); 43 - contentEl.addClass("atmark-modal"); 43 + contentEl.addClass("atmosphere-modal"); 44 44 45 45 contentEl.createEl("h2", { text: "Edit collections" }); 46 46 ··· 60 60 loading.remove(); 61 61 62 62 if (!collectionsResp.ok) { 63 - contentEl.createEl("p", { text: "Failed to load collections.", cls: "atmark-error" }); 63 + contentEl.createEl("p", { text: "Failed to load collections.", cls: "atmosphere-error" }); 64 64 return; 65 65 } 66 66 ··· 89 89 } catch (err) { 90 90 loading.remove(); 91 91 const message = err instanceof Error ? err.message : String(err); 92 - contentEl.createEl("p", { text: `Error: ${message}`, cls: "atmark-error" }); 92 + contentEl.createEl("p", { text: `Error: ${message}`, cls: "atmosphere-error" }); 93 93 } 94 94 } 95 95 96 96 private renderCollectionList(contentEl: HTMLElement) { 97 - const list = contentEl.createEl("div", { cls: "atmark-collection-list" }); 97 + const list = contentEl.createEl("div", { cls: "atmosphere-collection-list" }); 98 98 99 99 for (const state of this.collectionStates) { 100 - const item = list.createEl("label", { cls: "atmark-collection-item" }); 100 + const item = list.createEl("label", { cls: "atmosphere-collection-item" }); 101 101 102 - const checkbox = item.createEl("input", { type: "checkbox", cls: "atmark-collection-checkbox" }); 102 + const checkbox = item.createEl("input", { type: "checkbox", cls: "atmosphere-collection-checkbox" }); 103 103 checkbox.checked = state.isSelected; 104 104 checkbox.addEventListener("change", () => { 105 105 state.isSelected = checkbox.checked; 106 106 this.updateSaveButton(); 107 107 }); 108 108 109 - const info = item.createEl("div", { cls: "atmark-collection-item-info" }); 110 - info.createEl("span", { text: state.collection.value.name, cls: "atmark-collection-item-name" }); 109 + const info = item.createEl("div", { cls: "atmosphere-collection-item-info" }); 110 + info.createEl("span", { text: state.collection.value.name, cls: "atmosphere-collection-item-name" }); 111 111 if (state.collection.value.description) { 112 - info.createEl("span", { text: state.collection.value.description, cls: "atmark-collection-item-desc" }); 112 + info.createEl("span", { text: state.collection.value.description, cls: "atmosphere-collection-item-desc" }); 113 113 } 114 114 } 115 115 116 - const actions = contentEl.createEl("div", { cls: "atmark-modal-actions" }); 116 + const actions = contentEl.createEl("div", { cls: "atmosphere-modal-actions" }); 117 117 118 - const deleteBtn = actions.createEl("button", { text: "Delete", cls: "atmark-btn atmark-btn-danger" }); 118 + const deleteBtn = actions.createEl("button", { text: "Delete", cls: "atmosphere-btn atmosphere-btn-danger" }); 119 119 deleteBtn.addEventListener("click", () => { this.confirmDelete(contentEl); }); 120 120 121 - actions.createEl("div", { cls: "atmark-spacer" }); 121 + actions.createEl("div", { cls: "atmosphere-spacer" }); 122 122 123 - const cancelBtn = actions.createEl("button", { text: "Cancel", cls: "atmark-btn atmark-btn-secondary" }); 123 + const cancelBtn = actions.createEl("button", { text: "Cancel", cls: "atmosphere-btn atmosphere-btn-secondary" }); 124 124 cancelBtn.addEventListener("click", () => { this.close(); }); 125 125 126 - const saveBtn = actions.createEl("button", { text: "Save", cls: "atmark-btn atmark-btn-primary" }); 127 - saveBtn.id = "atmark-save-btn"; 126 + const saveBtn = actions.createEl("button", { text: "Save", cls: "atmosphere-btn atmosphere-btn-primary" }); 127 + saveBtn.id = "atmosphere-save-btn"; 128 128 saveBtn.disabled = true; 129 129 saveBtn.addEventListener("click", () => { void this.saveChanges(); }); 130 130 } ··· 132 132 private confirmDelete(contentEl: HTMLElement) { 133 133 contentEl.empty(); 134 134 contentEl.createEl("h2", { text: "Delete card" }); 135 - contentEl.createEl("p", { text: "Delete this card?", cls: "atmark-warning-text" }); 135 + contentEl.createEl("p", { text: "Delete this card?", cls: "atmosphere-warning-text" }); 136 136 137 - const actions = contentEl.createEl("div", { cls: "atmark-modal-actions" }); 137 + const actions = contentEl.createEl("div", { cls: "atmosphere-modal-actions" }); 138 138 139 - const cancelBtn = actions.createEl("button", { text: "Cancel", cls: "atmark-btn atmark-btn-secondary" }); 139 + const cancelBtn = actions.createEl("button", { text: "Cancel", cls: "atmosphere-btn atmosphere-btn-secondary" }); 140 140 cancelBtn.addEventListener("click", () => { 141 141 void this.onOpen(); 142 142 }); 143 143 144 - const confirmBtn = actions.createEl("button", { text: "Delete", cls: "atmark-btn atmark-btn-danger" }); 144 + const confirmBtn = actions.createEl("button", { text: "Delete", cls: "atmosphere-btn atmosphere-btn-danger" }); 145 145 confirmBtn.addEventListener("click", () => { void this.deleteCard(); }); 146 146 } 147 147 ··· 156 156 const rkey = this.cardUri.split("/").pop(); 157 157 if (!rkey) { 158 158 contentEl.empty(); 159 - contentEl.createEl("p", { text: "Invalid card uri.", cls: "atmark-error" }); 159 + contentEl.createEl("p", { text: "Invalid card uri.", cls: "atmosphere-error" }); 160 160 return; 161 161 } 162 162 ··· 173 173 } catch (err) { 174 174 contentEl.empty(); 175 175 const message = err instanceof Error ? err.message : String(err); 176 - contentEl.createEl("p", { text: `Failed to delete: ${message}`, cls: "atmark-error" }); 176 + contentEl.createEl("p", { text: `Failed to delete: ${message}`, cls: "atmosphere-error" }); 177 177 } 178 178 } 179 179 180 180 private updateSaveButton() { 181 - const saveBtn = document.getElementById("atmark-save-btn") as HTMLButtonElement | null; 181 + const saveBtn = document.getElementById("atmosphere-save-btn") as HTMLButtonElement | null; 182 182 if (!saveBtn) return; 183 183 184 184 const hasChanges = this.collectionStates.some(s => s.isSelected !== s.wasSelected); ··· 248 248 } catch (err) { 249 249 contentEl.empty(); 250 250 const message = err instanceof Error ? err.message : String(err); 251 - contentEl.createEl("p", { text: `Failed to save: ${message}`, cls: "atmark-error" }); 251 + contentEl.createEl("p", { text: `Failed to save: ${message}`, cls: "atmosphere-error" }); 252 252 } 253 253 } 254 254
+32 -32
src/components/editMarginBookmarkModal.ts
··· 3 3 import type { Main as MarginBookmark } from "../lexicons/types/at/margin/bookmark"; 4 4 import type { Main as MarginCollection } from "../lexicons/types/at/margin/collection"; 5 5 import type { Main as MarginCollectionItem } from "../lexicons/types/at/margin/collectionItem"; 6 - import type ATmarkPlugin from "../main"; 6 + import type AtmospherePlugin from "../main"; 7 7 import { putRecord, deleteRecord, getMarginCollections, getMarginCollectionItems, createMarginCollectionItem, getMarginBookmarks } from "../lib"; 8 8 9 9 type MarginBookmarkRecord = Record & { value: MarginBookmark }; ··· 23 23 } 24 24 25 25 export class EditMarginBookmarkModal extends Modal { 26 - plugin: ATmarkPlugin; 26 + plugin: AtmospherePlugin; 27 27 record: MarginBookmarkRecord; 28 28 onSuccess?: () => void; 29 29 tagStates: TagState[] = []; 30 30 newTagInput: HTMLInputElement | null = null; 31 31 collectionStates: CollectionState[] = []; 32 32 33 - constructor(plugin: ATmarkPlugin, record: MarginBookmarkRecord, onSuccess?: () => void) { 33 + constructor(plugin: AtmospherePlugin, record: MarginBookmarkRecord, onSuccess?: () => void) { 34 34 super(plugin.app); 35 35 this.plugin = plugin; 36 36 this.record = record; ··· 40 40 async onOpen() { 41 41 const { contentEl } = this; 42 42 contentEl.empty(); 43 - contentEl.addClass("atmark-modal"); 43 + contentEl.addClass("atmosphere-modal"); 44 44 45 45 contentEl.createEl("h2", { text: "Edit margin bookmark" }); 46 46 ··· 96 96 } catch (err) { 97 97 loading.remove(); 98 98 const message = err instanceof Error ? err.message : String(err); 99 - contentEl.createEl("p", { text: `Error: ${message}`, cls: "atmark-error" }); 99 + contentEl.createEl("p", { text: `Error: ${message}`, cls: "atmosphere-error" }); 100 100 } 101 101 } 102 102 103 103 private renderForm(contentEl: HTMLElement) { 104 - const form = contentEl.createEl("div", { cls: "atmark-form" }); 104 + const form = contentEl.createEl("div", { cls: "atmosphere-form" }); 105 105 106 - const tagsGroup = form.createEl("div", { cls: "atmark-form-group" }); 106 + const tagsGroup = form.createEl("div", { cls: "atmosphere-form-group" }); 107 107 tagsGroup.createEl("label", { text: "Tags" }); 108 108 109 - const tagsList = tagsGroup.createEl("div", { cls: "atmark-tag-list" }); 109 + const tagsList = tagsGroup.createEl("div", { cls: "atmosphere-tag-list" }); 110 110 for (const state of this.tagStates) { 111 111 this.addTagChip(tagsList, state); 112 112 } 113 113 114 - const newTagRow = tagsGroup.createEl("div", { cls: "atmark-tag-row" }); 114 + const newTagRow = tagsGroup.createEl("div", { cls: "atmosphere-tag-row" }); 115 115 this.newTagInput = newTagRow.createEl("input", { 116 116 type: "text", 117 - cls: "atmark-input", 117 + cls: "atmosphere-input", 118 118 attr: { placeholder: "Add new tag..." } 119 119 }); 120 120 const addBtn = newTagRow.createEl("button", { 121 121 text: "Add", 122 - cls: "atmark-btn atmark-btn-secondary", 122 + cls: "atmosphere-btn atmosphere-btn-secondary", 123 123 attr: { type: "button" } 124 124 }); 125 125 addBtn.addEventListener("click", () => { ··· 133 133 }); 134 134 135 135 if (this.collectionStates.length > 0) { 136 - const collectionsGroup = form.createEl("div", { cls: "atmark-form-group" }); 136 + const collectionsGroup = form.createEl("div", { cls: "atmosphere-form-group" }); 137 137 collectionsGroup.createEl("label", { text: "Collections" }); 138 138 139 - const collectionsList = collectionsGroup.createEl("div", { cls: "atmark-collection-list" }); 139 + const collectionsList = collectionsGroup.createEl("div", { cls: "atmosphere-collection-list" }); 140 140 141 141 for (const state of this.collectionStates) { 142 - const item = collectionsList.createEl("label", { cls: "atmark-collection-item" }); 142 + const item = collectionsList.createEl("label", { cls: "atmosphere-collection-item" }); 143 143 144 - const checkbox = item.createEl("input", { type: "checkbox", cls: "atmark-collection-checkbox" }); 144 + const checkbox = item.createEl("input", { type: "checkbox", cls: "atmosphere-collection-checkbox" }); 145 145 checkbox.checked = state.isSelected; 146 146 checkbox.addEventListener("change", () => { 147 147 state.isSelected = checkbox.checked; 148 148 }); 149 149 150 - const info = item.createEl("div", { cls: "atmark-collection-item-info" }); 151 - info.createEl("span", { text: state.collection.value.name, cls: "atmark-collection-item-name" }); 150 + const info = item.createEl("div", { cls: "atmosphere-collection-item-info" }); 151 + info.createEl("span", { text: state.collection.value.name, cls: "atmosphere-collection-item-name" }); 152 152 if (state.collection.value.description) { 153 - info.createEl("span", { text: state.collection.value.description, cls: "atmark-collection-item-desc" }); 153 + info.createEl("span", { text: state.collection.value.description, cls: "atmosphere-collection-item-desc" }); 154 154 } 155 155 } 156 156 } 157 157 158 - const actions = contentEl.createEl("div", { cls: "atmark-modal-actions" }); 158 + const actions = contentEl.createEl("div", { cls: "atmosphere-modal-actions" }); 159 159 160 160 const deleteBtn = actions.createEl("button", { 161 161 text: "Delete", 162 - cls: "atmark-btn atmark-btn-danger" 162 + cls: "atmosphere-btn atmosphere-btn-danger" 163 163 }); 164 164 deleteBtn.addEventListener("click", () => { this.confirmDelete(contentEl); }); 165 165 166 - actions.createEl("div", { cls: "atmark-spacer" }); 166 + actions.createEl("div", { cls: "atmosphere-spacer" }); 167 167 168 168 const cancelBtn = actions.createEl("button", { 169 169 text: "Cancel", 170 - cls: "atmark-btn atmark-btn-secondary" 170 + cls: "atmosphere-btn atmosphere-btn-secondary" 171 171 }); 172 172 cancelBtn.addEventListener("click", () => { this.close(); }); 173 173 174 174 const saveBtn = actions.createEl("button", { 175 175 text: "Save", 176 - cls: "atmark-btn atmark-btn-primary" 176 + cls: "atmosphere-btn atmosphere-btn-primary" 177 177 }); 178 178 saveBtn.addEventListener("click", () => { void this.saveChanges(); }); 179 179 } 180 180 181 181 private addTagChip(container: HTMLElement, state: TagState) { 182 - const item = container.createEl("label", { cls: "atmark-tag-item" }); 182 + const item = container.createEl("label", { cls: "atmosphere-tag-item" }); 183 183 const checkbox = item.createEl("input", { type: "checkbox" }); 184 184 checkbox.checked = state.isSelected; 185 185 checkbox.addEventListener("change", () => { ··· 191 191 private confirmDelete(contentEl: HTMLElement) { 192 192 contentEl.empty(); 193 193 contentEl.createEl("h2", { text: "Delete bookmark" }); 194 - contentEl.createEl("p", { text: "Delete this bookmark?", cls: "atmark-warning-text" }); 194 + contentEl.createEl("p", { text: "Delete this bookmark?", cls: "atmosphere-warning-text" }); 195 195 196 - const actions = contentEl.createEl("div", { cls: "atmark-modal-actions" }); 196 + const actions = contentEl.createEl("div", { cls: "atmosphere-modal-actions" }); 197 197 198 198 const cancelBtn = actions.createEl("button", { 199 199 text: "Cancel", 200 - cls: "atmark-btn atmark-btn-secondary" 200 + cls: "atmosphere-btn atmosphere-btn-secondary" 201 201 }); 202 202 cancelBtn.addEventListener("click", () => { 203 203 void this.onOpen(); ··· 205 205 206 206 const confirmBtn = actions.createEl("button", { 207 207 text: "Delete", 208 - cls: "atmark-btn atmark-btn-danger" 208 + cls: "atmosphere-btn atmosphere-btn-danger" 209 209 }); 210 210 confirmBtn.addEventListener("click", () => { void this.deleteBookmark(); }); 211 211 } ··· 221 221 const rkey = this.record.uri.split("/").pop(); 222 222 if (!rkey) { 223 223 contentEl.empty(); 224 - contentEl.createEl("p", { text: "Invalid bookmark uri.", cls: "atmark-error" }); 224 + contentEl.createEl("p", { text: "Invalid bookmark uri.", cls: "atmosphere-error" }); 225 225 return; 226 226 } 227 227 ··· 238 238 } catch (err) { 239 239 contentEl.empty(); 240 240 const message = err instanceof Error ? err.message : String(err); 241 - contentEl.createEl("p", { text: `Failed to delete: ${message}`, cls: "atmark-error" }); 241 + contentEl.createEl("p", { text: `Failed to delete: ${message}`, cls: "atmosphere-error" }); 242 242 } 243 243 } 244 244 ··· 260 260 const rkey = this.record.uri.split("/").pop(); 261 261 if (!rkey) { 262 262 contentEl.empty(); 263 - contentEl.createEl("p", { text: "Invalid bookmark uri.", cls: "atmark-error" }); 263 + contentEl.createEl("p", { text: "Invalid bookmark uri.", cls: "atmosphere-error" }); 264 264 return; 265 265 } 266 266 ··· 321 321 } catch (err) { 322 322 contentEl.empty(); 323 323 const message = err instanceof Error ? err.message : String(err); 324 - contentEl.createEl("p", { text: `Failed to save: ${message}`, cls: "atmark-error" }); 324 + contentEl.createEl("p", { text: `Failed to save: ${message}`, cls: "atmosphere-error" }); 325 325 } 326 326 } 327 327
+8 -8
src/components/profileIcon.ts
··· 30 30 profile: ProfileData | null, 31 31 onClick?: () => void 32 32 ): HTMLElement { 33 - const wrapper = container.createEl("div", { cls: "atmark-profile-icon" }); 33 + const wrapper = container.createEl("div", { cls: "atmosphere-profile-icon" }); 34 34 35 35 if (!profile) { 36 36 // Fallback when no profile data 37 - const placeholder = wrapper.createEl("div", { cls: "atmark-avatar-placeholder" }); 37 + const placeholder = wrapper.createEl("div", { cls: "atmosphere-avatar-placeholder" }); 38 38 placeholder.createEl("span", { text: "?" }); 39 39 return wrapper; 40 40 } 41 41 42 - const avatarBtn = wrapper.createEl("button", { cls: "atmark-avatar-btn" }); 42 + const avatarBtn = wrapper.createEl("button", { cls: "atmosphere-avatar-btn" }); 43 43 44 44 if (profile.avatar) { 45 - const img = avatarBtn.createEl("img", { cls: "atmark-avatar-img" }); 45 + const img = avatarBtn.createEl("img", { cls: "atmosphere-avatar-img" }); 46 46 img.src = profile.avatar; 47 47 img.alt = profile.displayName || profile.handle; 48 48 } else { ··· 53 53 .slice(0, 2) 54 54 .join("") 55 55 .toUpperCase(); 56 - avatarBtn.createEl("span", { text: initials, cls: "atmark-avatar-initials" }); 56 + avatarBtn.createEl("span", { text: initials, cls: "atmosphere-avatar-initials" }); 57 57 } 58 58 59 - const info = wrapper.createEl("div", { cls: "atmark-profile-info" }); 59 + const info = wrapper.createEl("div", { cls: "atmosphere-profile-info" }); 60 60 61 61 if (profile.displayName) { 62 - info.createEl("span", { text: profile.displayName, cls: "atmark-profile-name" }); 62 + info.createEl("span", { text: profile.displayName, cls: "atmosphere-profile-name" }); 63 63 } 64 64 65 - info.createEl("span", { text: `@${profile.handle}`, cls: "atmark-profile-handle" }); 65 + info.createEl("span", { text: `@${profile.handle}`, cls: "atmosphere-profile-handle" }); 66 66 67 67 if (onClick) { 68 68 avatarBtn.addEventListener("click", onClick);
+12 -12
src/components/selectPublicationModal.ts
··· 1 1 import { Modal } from "obsidian"; 2 - import type ATmarkPlugin from "../main"; 2 + import type AtmospherePlugin from "../main"; 3 3 import { getPublications } from "../lib"; 4 4 import { SiteStandardPublication } from "@atcute/standard-site"; 5 5 import type { ResourceUri } from "@atcute/lexicons"; ··· 10 10 }; 11 11 12 12 export class SelectPublicationModal extends Modal { 13 - plugin: ATmarkPlugin; 13 + plugin: AtmospherePlugin; 14 14 onSelect: (selection: PublicationSelection) => void; 15 15 16 - constructor(plugin: ATmarkPlugin, onSelect: (selection: PublicationSelection) => void) { 16 + constructor(plugin: AtmospherePlugin, onSelect: (selection: PublicationSelection) => void) { 17 17 super(plugin.app); 18 18 this.plugin = plugin; 19 19 this.onSelect = onSelect; ··· 22 22 async onOpen() { 23 23 const { contentEl } = this; 24 24 contentEl.empty(); 25 - contentEl.addClass("atmark-modal"); 25 + contentEl.addClass("atmosphere-modal"); 26 26 27 27 contentEl.createEl("h2", { text: "Select publication" }); 28 28 29 29 if (!this.plugin.client) { 30 - contentEl.createEl("p", { text: "Not logged in", cls: "atmark-error" }); 30 + contentEl.createEl("p", { text: "Not logged in", cls: "atmosphere-error" }); 31 31 return; 32 32 } 33 33 ··· 45 45 } 46 46 47 47 // Create a list of publications 48 - const listContainer = contentEl.createEl("div", { cls: "atmark-collection-list" }); 48 + const listContainer = contentEl.createEl("div", { cls: "atmosphere-collection-list" }); 49 49 50 50 for (const pub of pubs) { 51 - const item = listContainer.createEl("div", { cls: "atmark-collection-item" }); 51 + const item = listContainer.createEl("div", { cls: "atmosphere-collection-item" }); 52 52 53 53 const publication = pub.value; 54 54 55 - const info = item.createEl("div", { cls: "atmark-collection-item-info" }); 56 - info.createEl("div", { text: publication.name, cls: "atmark-collection-item-name" }); 55 + const info = item.createEl("div", { cls: "atmosphere-collection-item-info" }); 56 + info.createEl("div", { text: publication.name, cls: "atmosphere-collection-item-name" }); 57 57 58 58 if (publication.description) { 59 59 info.createEl("div", { 60 60 text: publication.description, 61 - cls: "atmark-collection-item-desc" 61 + cls: "atmosphere-collection-item-desc" 62 62 }); 63 63 } 64 64 65 65 info.createEl("div", { 66 66 text: publication.url, 67 - cls: "atmark-collection-item-desc" 67 + cls: "atmosphere-collection-item-desc" 68 68 }); 69 69 70 70 item.addEventListener("click", () => { ··· 79 79 } catch (error) { 80 80 loading.remove(); 81 81 const message = error instanceof Error ? error.message : String(error); 82 - contentEl.createEl("p", { text: `Error: ${message}`, cls: "atmark-error" }); 82 + contentEl.createEl("p", { text: `Error: ${message}`, cls: "atmosphere-error" }); 83 83 } 84 84 } 85 85
+3 -5
src/lib/clipper.ts
··· 3 3 import { Main as Publication } from "@atcute/standard-site/types/publication"; 4 4 import { is, parseResourceUri } from "@atcute/lexicons"; 5 5 import { Notice, TFile } from "obsidian"; 6 - import ATmarkPlugin from "main"; 6 + import AtmospherePlugin from "main"; 7 7 import { leafletContentToMarkdown } from "./markdown/leaflet"; 8 8 import { pcktContentToMarkdown } from "./markdown/pckt"; 9 9 import { ResolvedActor } from "@atcute/identity-resolver"; ··· 16 16 } 17 17 18 18 export class Clipper { 19 - plugin: ATmarkPlugin; 19 + plugin: AtmospherePlugin; 20 20 21 - constructor(plugin: ATmarkPlugin) { 21 + constructor(plugin: AtmospherePlugin) { 22 22 this.plugin = plugin; 23 23 } 24 24 ··· 50 50 if (actor && actor.handle) { 51 51 fm["author"] = `[${actor.handle}](${bskyLink(actor.handle)})`; 52 52 } 53 - fm["aturi"] = doc.uri; 54 - 55 53 let docUrl = ""; 56 54 57 55 // pubUrl is at:// record uri or https:// for loose document
+20 -24
src/main.ts
··· 1 1 import { Plugin, WorkspaceLeaf } from "obsidian"; 2 2 import { DEFAULT_SETTINGS, AtProtoSettings, SettingTab } from "./settings"; 3 - import { ATmarkView, VIEW_TYPE_ATMARK } from "./views/atmark"; 3 + import { AtmosphereView, VIEW_TYPE_ATMOSPHERE_BOOKMARKS } from "./views/bookmarks"; 4 4 import { publishFileAsDocument } from "./commands/publishDocument"; 5 - import { StandardFeedView, VIEW_STANDARD_FEED } from "views/standardfeed"; 5 + import { StandardFeedView, VIEW_ATMOSPHERE_STANDARD_FEED } from "views/standardfeed"; 6 6 import { ATClient } from "lib/client"; 7 7 import { Clipper } from "lib/clipper"; 8 8 9 - export default class ATmarkPlugin extends Plugin { 9 + export default class AtmospherePlugin extends Plugin { 10 10 settings: AtProtoSettings = DEFAULT_SETTINGS; 11 11 client: ATClient; 12 12 clipper: Clipper; ··· 21 21 this.client = new ATClient(creds); 22 22 this.clipper = new Clipper(this); 23 23 24 - this.registerView(VIEW_TYPE_ATMARK, (leaf) => { 25 - return new ATmarkView(leaf, this); 24 + this.registerView(VIEW_TYPE_ATMOSPHERE_BOOKMARKS, (leaf) => { 25 + return new AtmosphereView(leaf, this); 26 26 }); 27 27 28 - this.registerView(VIEW_STANDARD_FEED, (leaf) => { 28 + this.registerView(VIEW_ATMOSPHERE_STANDARD_FEED, (leaf) => { 29 29 return new StandardFeedView(leaf, this); 30 30 }); 31 31 32 - // included name of the plugin, which contains the acronym "AT" 33 - // eslint-disable-next-line obsidianmd/ui/sentence-case 34 - this.addRibbonIcon("layers", "ATmark bookmarks", () => { 35 - void this.activateView(VIEW_TYPE_ATMARK); 32 + this.addRibbonIcon("layers", "Atmosphere bookmarks", () => { 33 + void this.activateView(VIEW_TYPE_ATMOSPHERE_BOOKMARKS); 36 34 }); 37 35 38 - // included name of the plugin, which contains the acronym "AT" 39 - // eslint-disable-next-line obsidianmd/ui/sentence-case 40 - this.addRibbonIcon("rss", "ATmark feed", () => { 41 - void this.activateView(VIEW_STANDARD_FEED); 36 + this.addRibbonIcon("rss", "Atmosphere feed", () => { 37 + void this.activateView(VIEW_ATMOSPHERE_STANDARD_FEED); 42 38 }); 43 39 44 40 this.addCommand({ 45 - id: "view", 46 - name: "Open view", 47 - callback: () => { void this.activateView(VIEW_TYPE_ATMARK); }, 41 + id: "open-bookmarks", 42 + name: "Open bookmarks", 43 + callback: () => { void this.activateView(VIEW_TYPE_ATMOSPHERE_BOOKMARKS); }, 48 44 }); 49 45 50 - // this.addCommand({ 51 - // id: "standard-site-view", 52 - // name: "View publications", 53 - // callback: () => { void this.activateView(VIEW_TYPE_STANDARD_SITE); }, 54 - // }); 46 + this.addCommand({ 47 + id: "open-feed", 48 + name: "Open feed", 49 + callback: () => { void this.activateView(VIEW_ATMOSPHERE_STANDARD_FEED); }, 50 + }); 55 51 56 52 this.addCommand({ 57 - id: "standard-site-publich-document", 58 - name: "Publish document", 53 + id: "publish-note", 54 + name: "Publish note", 59 55 editorCheckCallback: (checking: boolean,) => { 60 56 const file = this.app.workspace.getActiveFile(); 61 57
+3 -3
src/settings.ts
··· 1 1 import { App, PluginSettingTab, Setting } from "obsidian"; 2 - import type ATmarkPlugin from "./main"; 2 + import type AtmospherePlugin from "./main"; 3 3 4 4 export interface AtProtoSettings { 5 5 identifier: string; ··· 14 14 }; 15 15 16 16 export class SettingTab extends PluginSettingTab { 17 - plugin: ATmarkPlugin; 17 + plugin: AtmospherePlugin; 18 18 19 - constructor(app: App, plugin: ATmarkPlugin) { 19 + constructor(app: App, plugin: AtmospherePlugin) { 20 20 super(app, plugin); 21 21 this.plugin = plugin; 22 22 }
+36 -36
src/sources/bookmark.ts
··· 1 1 import type { Client } from "@atcute/client"; 2 2 import type { Record } from "@atcute/atproto/types/repo/listRecords"; 3 3 import { setIcon } from "obsidian"; 4 - import type ATmarkPlugin from "../main"; 4 + import type AtmospherePlugin from "../main"; 5 5 import { getBookmarks } from "../lib"; 6 - import type { ATmarkItem, DataSource, SourceFilter } from "./types"; 6 + import type { ATBookmarkItem, DataSource, SourceFilter } from "./types"; 7 7 import { EditBookmarkModal } from "../components/editBookmarkModal"; 8 8 import { CreateTagModal } from "../components/createTagModal"; 9 9 import type { Main as Bookmark } from "../lexicons/types/community/lexicon/bookmarks/bookmark"; 10 10 11 11 type BookmarkRecord = Record & { value: Bookmark }; 12 12 13 - class BookmarkItem implements ATmarkItem { 13 + class BookmarkItem implements ATBookmarkItem { 14 14 private record: BookmarkRecord; 15 - private plugin: ATmarkPlugin; 15 + private plugin: AtmospherePlugin; 16 16 17 - constructor(record: BookmarkRecord, plugin: ATmarkPlugin) { 17 + constructor(record: BookmarkRecord, plugin: AtmospherePlugin) { 18 18 this.record = record; 19 19 this.plugin = plugin; 20 20 } ··· 48 48 } 49 49 50 50 render(container: HTMLElement): void { 51 - const el = container.createEl("div", { cls: "atmark-item-content" }); 51 + const el = container.createEl("div", { cls: "atmosphere-item-content" }); 52 52 const bookmark = this.record.value; 53 53 const enriched = bookmark.enriched; 54 54 55 55 if (bookmark.tags && bookmark.tags.length > 0) { 56 - const tagsContainer = el.createEl("div", { cls: "atmark-item-tags" }); 56 + const tagsContainer = el.createEl("div", { cls: "atmosphere-item-tags" }); 57 57 for (const tag of bookmark.tags) { 58 - tagsContainer.createEl("span", { text: tag, cls: "atmark-tag" }); 58 + tagsContainer.createEl("span", { text: tag, cls: "atmosphere-tag" }); 59 59 } 60 60 } 61 61 62 62 const title = enriched?.title || bookmark.title; 63 63 if (title) { 64 - el.createEl("div", { text: title, cls: "atmark-item-title" }); 64 + el.createEl("div", { text: title, cls: "atmosphere-item-title" }); 65 65 } 66 66 67 67 const imageUrl = enriched?.image || enriched?.thumb; 68 68 if (imageUrl) { 69 - const img = el.createEl("img", { cls: "atmark-item-image" }); 69 + const img = el.createEl("img", { cls: "atmosphere-item-image" }); 70 70 img.src = imageUrl; 71 71 img.alt = title || "Image"; 72 72 } ··· 76 76 const desc = description.length > 200 77 77 ? description.slice(0, 200) + "…" 78 78 : description; 79 - el.createEl("p", { text: desc, cls: "atmark-item-desc" }); 79 + el.createEl("p", { text: desc, cls: "atmosphere-item-desc" }); 80 80 } 81 81 82 82 if (enriched?.siteName) { 83 - el.createEl("span", { text: enriched.siteName, cls: "atmark-item-site" }); 83 + el.createEl("span", { text: enriched.siteName, cls: "atmosphere-item-site" }); 84 84 } 85 85 86 86 const link = el.createEl("a", { 87 87 text: bookmark.subject, 88 88 href: bookmark.subject, 89 - cls: "atmark-item-url", 89 + cls: "atmosphere-item-url", 90 90 }); 91 91 link.setAttr("target", "_blank"); 92 92 } 93 93 94 94 renderDetail(container: HTMLElement): void { 95 - const body = container.createEl("div", { cls: "atmark-detail-body" }); 95 + const body = container.createEl("div", { cls: "atmosphere-detail-body" }); 96 96 const bookmark = this.record.value; 97 97 const enriched = bookmark.enriched; 98 98 99 99 const title = enriched?.title || bookmark.title; 100 100 if (title) { 101 - body.createEl("h2", { text: title, cls: "atmark-detail-title" }); 101 + body.createEl("h2", { text: title, cls: "atmosphere-detail-title" }); 102 102 } 103 103 104 104 const imageUrl = enriched?.image || enriched?.thumb; 105 105 if (imageUrl) { 106 - const img = body.createEl("img", { cls: "atmark-detail-image" }); 106 + const img = body.createEl("img", { cls: "atmosphere-detail-image" }); 107 107 img.src = imageUrl; 108 108 img.alt = title || "Image"; 109 109 } 110 110 111 111 const description = enriched?.description || bookmark.description; 112 112 if (description) { 113 - body.createEl("p", { text: description, cls: "atmark-detail-description" }); 113 + body.createEl("p", { text: description, cls: "atmosphere-detail-description" }); 114 114 } 115 115 116 116 if (enriched?.siteName) { 117 - const metaGrid = body.createEl("div", { cls: "atmark-detail-meta" }); 118 - const item = metaGrid.createEl("div", { cls: "atmark-detail-meta-item" }); 119 - item.createEl("span", { text: "Site", cls: "atmark-detail-meta-label" }); 120 - item.createEl("span", { text: enriched.siteName, cls: "atmark-detail-meta-value" }); 117 + const metaGrid = body.createEl("div", { cls: "atmosphere-detail-meta" }); 118 + const item = metaGrid.createEl("div", { cls: "atmosphere-detail-meta-item" }); 119 + item.createEl("span", { text: "Site", cls: "atmosphere-detail-meta-label" }); 120 + item.createEl("span", { text: enriched.siteName, cls: "atmosphere-detail-meta-value" }); 121 121 } 122 122 123 - const linkWrapper = body.createEl("div", { cls: "atmark-detail-link-wrapper" }); 123 + const linkWrapper = body.createEl("div", { cls: "atmosphere-detail-link-wrapper" }); 124 124 const link = linkWrapper.createEl("a", { 125 125 text: bookmark.subject, 126 126 href: bookmark.subject, 127 - cls: "atmark-detail-link", 127 + cls: "atmosphere-detail-link", 128 128 }); 129 129 link.setAttr("target", "_blank"); 130 130 131 131 if (bookmark.tags && bookmark.tags.length > 0) { 132 - const tagsSection = container.createEl("div", { cls: "atmark-item-tags-section" }); 133 - tagsSection.createEl("h3", { text: "Tags", cls: "atmark-detail-section-title" }); 134 - const tagsContainer = tagsSection.createEl("div", { cls: "atmark-item-tags" }); 132 + const tagsSection = container.createEl("div", { cls: "atmosphere-item-tags-section" }); 133 + tagsSection.createEl("h3", { text: "Tags", cls: "atmosphere-detail-section-title" }); 134 + const tagsContainer = tagsSection.createEl("div", { cls: "atmosphere-item-tags" }); 135 135 for (const tag of bookmark.tags) { 136 - tagsContainer.createEl("span", { text: tag, cls: "atmark-tag" }); 136 + tagsContainer.createEl("span", { text: tag, cls: "atmosphere-tag" }); 137 137 } 138 138 } 139 139 } ··· 157 157 this.repo = repo; 158 158 } 159 159 160 - async fetchItems(filters: SourceFilter[], plugin: ATmarkPlugin): Promise<ATmarkItem[]> { 160 + async fetchItems(filters: SourceFilter[], plugin: AtmospherePlugin): Promise<ATBookmarkItem[]> { 161 161 const bookmarksResp = await getBookmarks(this.client, this.repo); 162 162 if (!bookmarksResp.ok) return []; 163 163 ··· 194 194 })); 195 195 } 196 196 197 - renderFilterUI(container: HTMLElement, activeFilters: Map<string, SourceFilter>, onChange: () => void, plugin: ATmarkPlugin): void { 198 - const section = container.createEl("div", { cls: "atmark-filter-section" }); 197 + renderFilterUI(container: HTMLElement, activeFilters: Map<string, SourceFilter>, onChange: () => void, plugin: AtmospherePlugin): void { 198 + const section = container.createEl("div", { cls: "atmosphere-filter-section" }); 199 199 200 - const titleRow = section.createEl("div", { cls: "atmark-filter-title-row" }); 201 - titleRow.createEl("h3", { text: "Tags", cls: "atmark-filter-title" }); 200 + const titleRow = section.createEl("div", { cls: "atmosphere-filter-title-row" }); 201 + titleRow.createEl("h3", { text: "Tags", cls: "atmosphere-filter-title" }); 202 202 203 - const createBtn = titleRow.createEl("button", { cls: "atmark-filter-create-btn" }); 203 + const createBtn = titleRow.createEl("button", { cls: "atmosphere-filter-create-btn" }); 204 204 setIcon(createBtn, "plus"); 205 205 createBtn.addEventListener("click", () => { 206 206 new CreateTagModal(plugin, onChange).open(); 207 207 }); 208 208 209 - const chips = section.createEl("div", { cls: "atmark-filter-chips" }); 209 + const chips = section.createEl("div", { cls: "atmosphere-filter-chips" }); 210 210 211 211 const allChip = chips.createEl("button", { 212 212 text: "All", 213 - cls: `atmark-chip ${!activeFilters.has("bookmarkTag") ? "atmark-chip-active" : ""}`, 213 + cls: `atmosphere-chip ${!activeFilters.has("bookmarkTag") ? "atmosphere-chip-active" : ""}`, 214 214 }); 215 215 allChip.addEventListener("click", () => { 216 216 activeFilters.delete("bookmarkTag"); ··· 221 221 for (const tag of tags) { 222 222 const chip = chips.createEl("button", { 223 223 text: tag.label, 224 - cls: `atmark-chip ${activeFilters.get("bookmarkTag")?.value === tag.value ? "atmark-chip-active" : ""}`, 224 + cls: `atmosphere-chip ${activeFilters.get("bookmarkTag")?.value === tag.value ? "atmosphere-chip-active" : ""}`, 225 225 }); 226 226 chip.addEventListener("click", () => { 227 227 activeFilters.set("bookmarkTag", tag);
+41 -41
src/sources/margin.ts
··· 1 1 import type { Client } from "@atcute/client"; 2 2 import type { Record } from "@atcute/atproto/types/repo/listRecords"; 3 3 import { setIcon } from "obsidian"; 4 - import type ATmarkPlugin from "../main"; 4 + import type AtmospherePlugin from "../main"; 5 5 import { getMarginBookmarks, getMarginCollections, getMarginCollectionItems } from "../lib"; 6 - import type { ATmarkItem, DataSource, SourceFilter } from "./types"; 6 + import type { ATBookmarkItem, DataSource, SourceFilter } from "./types"; 7 7 import type { Main as MarginBookmark } from "../lexicons/types/at/margin/bookmark"; 8 8 import type { Main as MarginCollection } from "../lexicons/types/at/margin/collection"; 9 9 import type { Main as MarginCollectionItem } from "../lexicons/types/at/margin/collectionItem"; ··· 14 14 type MarginCollectionRecord = Record & { value: MarginCollection }; 15 15 type MarginCollectionItemRecord = Record & { value: MarginCollectionItem }; 16 16 17 - class MarginItem implements ATmarkItem { 17 + class MarginItem implements ATBookmarkItem { 18 18 private record: MarginBookmarkRecord; 19 - private plugin: ATmarkPlugin; 19 + private plugin: AtmospherePlugin; 20 20 private collections: Array<{ uri: string; name: string }>; 21 21 22 - constructor(record: MarginBookmarkRecord, collections: Array<{ uri: string; name: string }>, plugin: ATmarkPlugin) { 22 + constructor(record: MarginBookmarkRecord, collections: Array<{ uri: string; name: string }>, plugin: AtmospherePlugin) { 23 23 this.record = record; 24 24 this.collections = collections; 25 25 this.plugin = plugin; ··· 54 54 } 55 55 56 56 render(container: HTMLElement): void { 57 - const el = container.createEl("div", { cls: "atmark-item-content" }); 57 + const el = container.createEl("div", { cls: "atmosphere-item-content" }); 58 58 const bookmark = this.record.value; 59 59 60 60 if (this.collections.length > 0) { 61 - const collectionsContainer = el.createEl("div", { cls: "atmark-item-collections" }); 61 + const collectionsContainer = el.createEl("div", { cls: "atmosphere-item-collections" }); 62 62 for (const collection of this.collections) { 63 - collectionsContainer.createEl("span", { text: collection.name, cls: "atmark-collection" }); 63 + collectionsContainer.createEl("span", { text: collection.name, cls: "atmosphere-collection" }); 64 64 } 65 65 } 66 66 67 67 if (bookmark.tags && bookmark.tags.length > 0) { 68 - const tagsContainer = el.createEl("div", { cls: "atmark-item-tags" }); 68 + const tagsContainer = el.createEl("div", { cls: "atmosphere-item-tags" }); 69 69 for (const tag of bookmark.tags) { 70 - tagsContainer.createEl("span", { text: tag, cls: "atmark-tag" }); 70 + tagsContainer.createEl("span", { text: tag, cls: "atmosphere-tag" }); 71 71 } 72 72 } 73 73 74 74 if (bookmark.title) { 75 - el.createEl("div", { text: bookmark.title, cls: "atmark-item-title" }); 75 + el.createEl("div", { text: bookmark.title, cls: "atmosphere-item-title" }); 76 76 } 77 77 78 78 if (bookmark.description) { 79 79 const desc = bookmark.description.length > 200 80 80 ? bookmark.description.slice(0, 200) + "…" 81 81 : bookmark.description; 82 - el.createEl("p", { text: desc, cls: "atmark-item-desc" }); 82 + el.createEl("p", { text: desc, cls: "atmosphere-item-desc" }); 83 83 } 84 84 85 85 const link = el.createEl("a", { 86 86 text: bookmark.source, 87 87 href: bookmark.source, 88 - cls: "atmark-item-url", 88 + cls: "atmosphere-item-url", 89 89 }); 90 90 link.setAttr("target", "_blank"); 91 91 } 92 92 93 93 renderDetail(container: HTMLElement): void { 94 - const body = container.createEl("div", { cls: "atmark-detail-body" }); 94 + const body = container.createEl("div", { cls: "atmosphere-detail-body" }); 95 95 const bookmark = this.record.value; 96 96 97 97 if (bookmark.title) { 98 - body.createEl("h2", { text: bookmark.title, cls: "atmark-detail-title" }); 98 + body.createEl("h2", { text: bookmark.title, cls: "atmosphere-detail-title" }); 99 99 } 100 100 101 101 if (bookmark.description) { 102 - body.createEl("p", { text: bookmark.description, cls: "atmark-detail-description" }); 102 + body.createEl("p", { text: bookmark.description, cls: "atmosphere-detail-description" }); 103 103 } 104 104 105 - const linkWrapper = body.createEl("div", { cls: "atmark-detail-link-wrapper" }); 105 + const linkWrapper = body.createEl("div", { cls: "atmosphere-detail-link-wrapper" }); 106 106 const link = linkWrapper.createEl("a", { 107 107 text: bookmark.source, 108 108 href: bookmark.source, 109 - cls: "atmark-detail-link", 109 + cls: "atmosphere-detail-link", 110 110 }); 111 111 link.setAttr("target", "_blank"); 112 112 113 113 if (this.collections.length > 0) { 114 - const collectionsSection = container.createEl("div", { cls: "atmark-item-collections-section" }); 115 - collectionsSection.createEl("h3", { text: "Collections", cls: "atmark-detail-section-title" }); 116 - const collectionsContainer = collectionsSection.createEl("div", { cls: "atmark-item-collections" }); 114 + const collectionsSection = container.createEl("div", { cls: "atmosphere-item-collections-section" }); 115 + collectionsSection.createEl("h3", { text: "Collections", cls: "atmosphere-detail-section-title" }); 116 + const collectionsContainer = collectionsSection.createEl("div", { cls: "atmosphere-item-collections" }); 117 117 for (const collection of this.collections) { 118 - collectionsContainer.createEl("span", { text: collection.name, cls: "atmark-collection" }); 118 + collectionsContainer.createEl("span", { text: collection.name, cls: "atmosphere-collection" }); 119 119 } 120 120 } 121 121 122 122 if (bookmark.tags && bookmark.tags.length > 0) { 123 - const tagsSection = container.createEl("div", { cls: "atmark-item-tags-section" }); 124 - tagsSection.createEl("h3", { text: "Tags", cls: "atmark-detail-section-title" }); 125 - const tagsContainer = tagsSection.createEl("div", { cls: "atmark-item-tags" }); 123 + const tagsSection = container.createEl("div", { cls: "atmosphere-item-tags-section" }); 124 + tagsSection.createEl("h3", { text: "Tags", cls: "atmosphere-detail-section-title" }); 125 + const tagsContainer = tagsSection.createEl("div", { cls: "atmosphere-item-tags" }); 126 126 for (const tag of bookmark.tags) { 127 - tagsContainer.createEl("span", { text: tag, cls: "atmark-tag" }); 127 + tagsContainer.createEl("span", { text: tag, cls: "atmosphere-tag" }); 128 128 } 129 129 } 130 130 } ··· 148 148 this.repo = repo; 149 149 } 150 150 151 - async fetchItems(filters: SourceFilter[], plugin: ATmarkPlugin): Promise<ATmarkItem[]> { 151 + async fetchItems(filters: SourceFilter[], plugin: AtmospherePlugin): Promise<ATBookmarkItem[]> { 152 152 const bookmarksResp = await getMarginBookmarks(this.client, this.repo); 153 153 if (!bookmarksResp.ok) return []; 154 154 ··· 238 238 return filters; 239 239 } 240 240 241 - renderFilterUI(container: HTMLElement, activeFilters: Map<string, SourceFilter>, onChange: () => void, plugin: ATmarkPlugin): void { 242 - const collectionsSection = container.createEl("div", { cls: "atmark-filter-section" }); 241 + renderFilterUI(container: HTMLElement, activeFilters: Map<string, SourceFilter>, onChange: () => void, plugin: AtmospherePlugin): void { 242 + const collectionsSection = container.createEl("div", { cls: "atmosphere-filter-section" }); 243 243 244 - const collectionsTitleRow = collectionsSection.createEl("div", { cls: "atmark-filter-title-row" }); 245 - collectionsTitleRow.createEl("h3", { text: "Collections", cls: "atmark-filter-title" }); 244 + const collectionsTitleRow = collectionsSection.createEl("div", { cls: "atmosphere-filter-title-row" }); 245 + collectionsTitleRow.createEl("h3", { text: "Collections", cls: "atmosphere-filter-title" }); 246 246 247 - const createCollectionBtn = collectionsTitleRow.createEl("button", { cls: "atmark-filter-create-btn" }); 247 + const createCollectionBtn = collectionsTitleRow.createEl("button", { cls: "atmosphere-filter-create-btn" }); 248 248 setIcon(createCollectionBtn, "plus"); 249 249 createCollectionBtn.addEventListener("click", () => { 250 250 new CreateMarginCollectionModal(plugin, onChange).open(); 251 251 }); 252 252 253 - const collectionsChips = collectionsSection.createEl("div", { cls: "atmark-filter-chips" }); 253 + const collectionsChips = collectionsSection.createEl("div", { cls: "atmosphere-filter-chips" }); 254 254 255 255 const allCollectionsChip = collectionsChips.createEl("button", { 256 256 text: "All", 257 - cls: `atmark-chip ${!activeFilters.has("marginCollection") ? "atmark-chip-active" : ""}`, 257 + cls: `atmosphere-chip ${!activeFilters.has("marginCollection") ? "atmosphere-chip-active" : ""}`, 258 258 }); 259 259 allCollectionsChip.addEventListener("click", () => { 260 260 activeFilters.delete("marginCollection"); 261 261 onChange(); 262 262 }); 263 263 264 - const tagsSection = container.createEl("div", { cls: "atmark-filter-section" }); 264 + const tagsSection = container.createEl("div", { cls: "atmosphere-filter-section" }); 265 265 266 - const tagsTitleRow = tagsSection.createEl("div", { cls: "atmark-filter-title-row" }); 267 - tagsTitleRow.createEl("h3", { text: "Tags", cls: "atmark-filter-title" }); 266 + const tagsTitleRow = tagsSection.createEl("div", { cls: "atmosphere-filter-title-row" }); 267 + tagsTitleRow.createEl("h3", { text: "Tags", cls: "atmosphere-filter-title" }); 268 268 269 - const tagsChips = tagsSection.createEl("div", { cls: "atmark-filter-chips" }); 269 + const tagsChips = tagsSection.createEl("div", { cls: "atmosphere-filter-chips" }); 270 270 271 271 const allTagsChip = tagsChips.createEl("button", { 272 272 text: "All", 273 - cls: `atmark-chip ${!activeFilters.has("marginTag") ? "atmark-chip-active" : ""}`, 273 + cls: `atmosphere-chip ${!activeFilters.has("marginTag") ? "atmosphere-chip-active" : ""}`, 274 274 }); 275 275 allTagsChip.addEventListener("click", () => { 276 276 activeFilters.delete("marginTag"); ··· 282 282 if (filter.type === "marginCollection") { 283 283 const chip = collectionsChips.createEl("button", { 284 284 text: filter.label, 285 - cls: `atmark-chip ${activeFilters.get("marginCollection")?.value === filter.value ? "atmark-chip-active" : ""}`, 285 + cls: `atmosphere-chip ${activeFilters.get("marginCollection")?.value === filter.value ? "atmosphere-chip-active" : ""}`, 286 286 }); 287 287 chip.addEventListener("click", () => { 288 288 activeFilters.set("marginCollection", filter); ··· 291 291 } else if (filter.type === "marginTag") { 292 292 const chip = tagsChips.createEl("button", { 293 293 text: filter.label, 294 - cls: `atmark-chip ${activeFilters.get("marginTag")?.value === filter.value ? "atmark-chip-active" : ""}`, 294 + cls: `atmosphere-chip ${activeFilters.get("marginTag")?.value === filter.value ? "atmosphere-chip-active" : ""}`, 295 295 }); 296 296 chip.addEventListener("click", () => { 297 297 activeFilters.set("marginTag", filter);
+32 -32
src/sources/semble.ts
··· 1 1 import type { Client } from "@atcute/client"; 2 2 import type { Record } from "@atcute/atproto/types/repo/listRecords"; 3 3 import { setIcon } from "obsidian"; 4 - import type ATmarkPlugin from "../main"; 4 + import type AtmospherePlugin from "../main"; 5 5 import { getCards, getCollections, getCollectionLinks } from "../lib"; 6 6 import type { Main as Card, NoteContent, UrlContent } from "../lexicons/types/network/cosmik/card"; 7 7 import type { Main as Collection } from "../lexicons/types/network/cosmik/collection"; 8 8 import type { Main as CollectionLink } from "../lexicons/types/network/cosmik/collectionLink"; 9 - import type { ATmarkItem, DataSource, SourceFilter } from "./types"; 9 + import type { ATBookmarkItem, DataSource, SourceFilter } from "./types"; 10 10 import { EditCardModal } from "../components/editCardModal"; 11 11 import { CreateCollectionModal } from "../components/createCollectionModal"; 12 12 ··· 14 14 type CollectionRecord = Record & { value: Collection }; 15 15 type CollectionLinkRecord = Record & { value: CollectionLink }; 16 16 17 - class SembleItem implements ATmarkItem { 17 + class SembleItem implements ATBookmarkItem { 18 18 private record: CardRecord; 19 19 private attachedNotes: Array<{ uri: string; text: string }>; 20 - private plugin: ATmarkPlugin; 20 + private plugin: AtmospherePlugin; 21 21 22 - constructor(record: CardRecord, attachedNotes: Array<{ uri: string; text: string }>, plugin: ATmarkPlugin) { 22 + constructor(record: CardRecord, attachedNotes: Array<{ uri: string; text: string }>, plugin: AtmospherePlugin) { 23 23 this.record = record; 24 24 this.attachedNotes = attachedNotes; 25 25 this.plugin = plugin; ··· 54 54 } 55 55 56 56 render(container: HTMLElement): void { 57 - const el = container.createEl("div", { cls: "atmark-item-content" }); 57 + const el = container.createEl("div", { cls: "atmosphere-item-content" }); 58 58 59 59 const card = this.record.value; 60 60 61 61 if (card.type === "NOTE") { 62 62 const content = card.content as NoteContent; 63 - el.createEl("p", { text: content.text, cls: "atmark-semble-card-text" }); 63 + el.createEl("p", { text: content.text, cls: "atmosphere-semble-card-text" }); 64 64 } else if (card.type === "URL") { 65 65 const content = card.content as UrlContent; 66 66 const meta = content.metadata; 67 67 68 68 if (meta?.title) { 69 - el.createEl("div", { text: meta.title, cls: "atmark-item-title" }); 69 + el.createEl("div", { text: meta.title, cls: "atmosphere-item-title" }); 70 70 } 71 71 72 72 if (meta?.imageUrl) { 73 - const img = el.createEl("img", { cls: "atmark-item-image" }); 73 + const img = el.createEl("img", { cls: "atmosphere-item-image" }); 74 74 img.src = meta.imageUrl; 75 75 img.alt = meta.title || "Image"; 76 76 } ··· 79 79 const desc = meta.description.length > 200 80 80 ? meta.description.slice(0, 200) + "…" 81 81 : meta.description; 82 - el.createEl("p", { text: desc, cls: "atmark-item-desc" }); 82 + el.createEl("p", { text: desc, cls: "atmosphere-item-desc" }); 83 83 } 84 84 85 85 if (meta?.siteName) { 86 - el.createEl("span", { text: meta.siteName, cls: "atmark-item-site" }); 86 + el.createEl("span", { text: meta.siteName, cls: "atmosphere-item-site" }); 87 87 } 88 88 89 89 const link = el.createEl("a", { 90 90 text: content.url, 91 91 href: content.url, 92 - cls: "atmark-item-url", 92 + cls: "atmosphere-item-url", 93 93 }); 94 94 link.setAttr("target", "_blank"); 95 95 } 96 96 } 97 97 98 98 renderDetail(container: HTMLElement): void { 99 - const body = container.createEl("div", { cls: "atmark-detail-body" }); 99 + const body = container.createEl("div", { cls: "atmosphere-detail-body" }); 100 100 const card = this.record.value; 101 101 102 102 if (card.type === "NOTE") { 103 103 const content = card.content as NoteContent; 104 - body.createEl("p", { text: content.text, cls: "atmark-semble-detail-text" }); 104 + body.createEl("p", { text: content.text, cls: "atmosphere-semble-detail-text" }); 105 105 } else if (card.type === "URL") { 106 106 const content = card.content as UrlContent; 107 107 const meta = content.metadata; 108 108 109 109 if (meta?.title) { 110 - body.createEl("h2", { text: meta.title, cls: "atmark-detail-title" }); 110 + body.createEl("h2", { text: meta.title, cls: "atmosphere-detail-title" }); 111 111 } 112 112 113 113 if (meta?.imageUrl) { 114 - const img = body.createEl("img", { cls: "atmark-detail-image" }); 114 + const img = body.createEl("img", { cls: "atmosphere-detail-image" }); 115 115 img.src = meta.imageUrl; 116 116 img.alt = meta.title || "Image"; 117 117 } 118 118 119 119 if (meta?.description) { 120 - body.createEl("p", { text: meta.description, cls: "atmark-detail-description" }); 120 + body.createEl("p", { text: meta.description, cls: "atmosphere-detail-description" }); 121 121 } 122 122 123 123 if (meta?.siteName) { 124 - const metaGrid = body.createEl("div", { cls: "atmark-detail-meta" }); 125 - const item = metaGrid.createEl("div", { cls: "atmark-detail-meta-item" }); 126 - item.createEl("span", { text: "Site", cls: "atmark-detail-meta-label" }); 127 - item.createEl("span", { text: meta.siteName, cls: "atmark-detail-meta-value" }); 124 + const metaGrid = body.createEl("div", { cls: "atmosphere-detail-meta" }); 125 + const item = metaGrid.createEl("div", { cls: "atmosphere-detail-meta-item" }); 126 + item.createEl("span", { text: "Site", cls: "atmosphere-detail-meta-label" }); 127 + item.createEl("span", { text: meta.siteName, cls: "atmosphere-detail-meta-value" }); 128 128 } 129 129 130 - const linkWrapper = body.createEl("div", { cls: "atmark-detail-link-wrapper" }); 130 + const linkWrapper = body.createEl("div", { cls: "atmosphere-detail-link-wrapper" }); 131 131 const link = linkWrapper.createEl("a", { 132 132 text: content.url, 133 133 href: content.url, 134 - cls: "atmark-detail-link", 134 + cls: "atmosphere-detail-link", 135 135 }); 136 136 link.setAttr("target", "_blank"); 137 137 } ··· 157 157 this.repo = repo; 158 158 } 159 159 160 - async fetchItems(filters: SourceFilter[], plugin: ATmarkPlugin): Promise<ATmarkItem[]> { 160 + async fetchItems(filters: SourceFilter[], plugin: AtmospherePlugin): Promise<ATBookmarkItem[]> { 161 161 const cardsResp = await getCards(this.client, this.repo); 162 162 if (!cardsResp.ok) return []; 163 163 ··· 215 215 })); 216 216 } 217 217 218 - renderFilterUI(container: HTMLElement, activeFilters: Map<string, SourceFilter>, onChange: () => void, plugin: ATmarkPlugin): void { 219 - const section = container.createEl("div", { cls: "atmark-filter-section" }); 218 + renderFilterUI(container: HTMLElement, activeFilters: Map<string, SourceFilter>, onChange: () => void, plugin: AtmospherePlugin): void { 219 + const section = container.createEl("div", { cls: "atmosphere-filter-section" }); 220 220 221 - const titleRow = section.createEl("div", { cls: "atmark-filter-title-row" }); 222 - titleRow.createEl("h3", { text: "Semble collections", cls: "atmark-filter-title" }); 221 + const titleRow = section.createEl("div", { cls: "atmosphere-filter-title-row" }); 222 + titleRow.createEl("h3", { text: "Semble collections", cls: "atmosphere-filter-title" }); 223 223 224 - const createBtn = titleRow.createEl("button", { cls: "atmark-filter-create-btn" }); 224 + const createBtn = titleRow.createEl("button", { cls: "atmosphere-filter-create-btn" }); 225 225 setIcon(createBtn, "plus"); 226 226 createBtn.addEventListener("click", () => { 227 227 new CreateCollectionModal(plugin, onChange).open(); 228 228 }); 229 229 230 - const chips = section.createEl("div", { cls: "atmark-filter-chips" }); 230 + const chips = section.createEl("div", { cls: "atmosphere-filter-chips" }); 231 231 232 232 const allChip = chips.createEl("button", { 233 233 text: "All", 234 - cls: `atmark-chip ${!activeFilters.has("sembleCollection") ? "atmark-chip-active" : ""}`, 234 + cls: `atmosphere-chip ${!activeFilters.has("sembleCollection") ? "atmosphere-chip-active" : ""}`, 235 235 }); 236 236 allChip.addEventListener("click", () => { 237 237 activeFilters.delete("sembleCollection"); ··· 244 244 for (const collection of collections) { 245 245 const chip = chips.createEl("button", { 246 246 text: collection.label, 247 - cls: `atmark-chip ${activeFilters.get("sembleCollection")?.value === collection.value ? "atmark-chip-active" : ""}`, 247 + cls: `atmosphere-chip ${activeFilters.get("sembleCollection")?.value === collection.value ? "atmosphere-chip-active" : ""}`, 248 248 }); 249 249 chip.addEventListener("click", () => { 250 250 activeFilters.set("sembleCollection", collection);
+4 -4
src/sources/types.ts
··· 1 - import type ATmarkPlugin from "../main"; 1 + import type AtmospherePlugin from "../main"; 2 2 3 - export interface ATmarkItem { 3 + export interface ATBookmarkItem { 4 4 render(container: HTMLElement): void; 5 5 renderDetail(container: HTMLElement): void; 6 6 canAddNotes(): boolean; ··· 21 21 22 22 export interface DataSource { 23 23 readonly name: "semble" | "bookmark" | "margin"; 24 - fetchItems(filters: SourceFilter[], plugin: ATmarkPlugin): Promise<ATmarkItem[]>; 24 + fetchItems(filters: SourceFilter[], plugin: AtmospherePlugin): Promise<ATBookmarkItem[]>; 25 25 getAvailableFilters(): Promise<SourceFilter[]>; 26 - renderFilterUI(container: HTMLElement, activeFilters: Map<string, SourceFilter>, onChange: () => void, plugin: ATmarkPlugin): void; 26 + renderFilterUI(container: HTMLElement, activeFilters: Map<string, SourceFilter>, onChange: () => void, plugin: AtmospherePlugin): void; 27 27 }
+30 -32
src/views/atmark.ts src/views/bookmarks.ts
··· 1 1 import { ItemView, WorkspaceLeaf, setIcon } from "obsidian"; 2 - import type ATmarkPlugin from "../main"; 2 + import type AtmospherePlugin from "../main"; 3 3 import { CardDetailModal } from "../components/cardDetailModal"; 4 - import type { ATmarkItem, DataSource, SourceFilter } from "../sources/types"; 4 + import type { ATBookmarkItem, DataSource, SourceFilter } from "../sources/types"; 5 5 import { SembleSource } from "../sources/semble"; 6 6 import { BookmarkSource } from "../sources/bookmark"; 7 7 import { MarginSource } from "../sources/margin"; 8 8 9 - export const VIEW_TYPE_ATMARK = "atmark-view"; 9 + export const VIEW_TYPE_ATMOSPHERE_BOOKMARKS = "atmosphere-bookmarks"; 10 10 11 11 type SourceType = "semble" | "bookmark" | "margin"; 12 12 13 - export class ATmarkView extends ItemView { 14 - plugin: ATmarkPlugin; 13 + export class AtmosphereView extends ItemView { 14 + plugin: AtmospherePlugin; 15 15 activeSource: SourceType = "semble"; 16 16 sources: Map<SourceType, { source: DataSource; filters: Map<string, SourceFilter> }> = new Map(); 17 17 18 - constructor(leaf: WorkspaceLeaf, plugin: ATmarkPlugin) { 18 + constructor(leaf: WorkspaceLeaf, plugin: AtmospherePlugin) { 19 19 super(leaf); 20 20 this.plugin = plugin; 21 21 } ··· 40 40 } 41 41 42 42 getViewType() { 43 - return VIEW_TYPE_ATMARK; 43 + return VIEW_TYPE_ATMOSPHERE_BOOKMARKS; 44 44 } 45 45 46 46 getDisplayText() { 47 - // This is the name of the plugin, which contains the acronym "AT" 48 - // eslint-disable-next-line obsidianmd/ui/sentence-case 49 - return "ATmark"; 47 + return "Atmosphere bookmarks"; 50 48 } 51 49 52 50 getIcon() { ··· 58 56 await this.render(); 59 57 } 60 58 61 - async fetchItems(): Promise<ATmarkItem[]> { 59 + async fetchItems(): Promise<ATBookmarkItem[]> { 62 60 if (!this.plugin.client) return []; 63 61 64 62 const sourceData = this.sources.get(this.activeSource); ··· 71 69 async render() { 72 70 const container = this.contentEl; 73 71 container.empty(); 74 - container.addClass("atmark-view"); 72 + container.addClass("atmosphere-view"); 75 73 76 74 this.renderHeader(container); 77 75 ··· 87 85 return; 88 86 } 89 87 90 - const grid = container.createEl("div", { cls: "atmark-grid" }); 88 + const grid = container.createEl("div", { cls: "atmosphere-grid" }); 91 89 for (const item of items) { 92 90 try { 93 91 this.renderItem(grid, item); ··· 99 97 } catch (err) { 100 98 loading.remove(); 101 99 const message = err instanceof Error ? err.message : String(err); 102 - container.createEl("p", { text: `Failed to load: ${message}`, cls: "atmark-error" }); 100 + container.createEl("p", { text: `Failed to load: ${message}`, cls: "atmosphere-error" }); 103 101 } 104 102 } 105 103 106 104 private renderHeader(container: HTMLElement) { 107 - const header = container.createEl("div", { cls: "atmark-header" }); 105 + const header = container.createEl("div", { cls: "atmosphere-header" }); 108 106 109 - const sourceSelector = header.createEl("div", { cls: "atmark-source-selector" }); 107 + const sourceSelector = header.createEl("div", { cls: "atmosphere-source-selector" }); 110 108 const sources: SourceType[] = ["semble", "margin", "bookmark"]; 111 109 112 110 for (const source of sources) { 113 - const label = sourceSelector.createEl("label", { cls: "atmark-source-option" }); 111 + const label = sourceSelector.createEl("label", { cls: "atmosphere-source-option" }); 114 112 115 113 const radio = label.createEl("input", { 116 114 type: "radio", 117 - cls: "atmark-source-radio", 115 + cls: "atmosphere-source-radio", 118 116 }); 119 - radio.name = "atmark-source"; 117 + radio.name = "atmosphere-source"; 120 118 radio.checked = this.activeSource === source; 121 119 radio.addEventListener("change", () => { 122 120 this.activeSource = source; ··· 125 123 126 124 label.createEl("span", { 127 125 text: source.charAt(0).toUpperCase() + source.slice(1), 128 - cls: "atmark-source-text", 126 + cls: "atmosphere-source-text", 129 127 }); 130 128 } 131 129 132 - const filtersContainer = container.createEl("div", { cls: "atmark-filters" }); 130 + const filtersContainer = container.createEl("div", { cls: "atmosphere-filters" }); 133 131 const sourceData = this.sources.get(this.activeSource); 134 132 if (sourceData) { 135 133 sourceData.source.renderFilterUI( ··· 141 139 } 142 140 } 143 141 144 - private renderItem(container: HTMLElement, item: ATmarkItem) { 145 - const el = container.createEl("div", { cls: "atmark-item" }); 142 + private renderItem(container: HTMLElement, item: ATBookmarkItem) { 143 + const el = container.createEl("div", { cls: "atmosphere-item" }); 146 144 147 145 el.addEventListener("click", (e) => { 148 146 // Don't open detail if clicking the edit button 149 - if ((e.target as HTMLElement).closest(".atmark-item-edit-btn")) { 147 + if ((e.target as HTMLElement).closest(".atmosphere-item-edit-btn")) { 150 148 return; 151 149 } 152 150 new CardDetailModal(this.plugin, item, () => { ··· 154 152 }).open(); 155 153 }); 156 154 157 - const header = el.createEl("div", { cls: "atmark-item-header" }); 155 + const header = el.createEl("div", { cls: "atmosphere-item-header" }); 158 156 const source = item.getSource(); 159 157 header.createEl("span", { 160 158 text: source, 161 - cls: `atmark-badge atmark-badge-${source}`, 159 + cls: `atmosphere-badge atmosphere-badge-${source}`, 162 160 }); 163 161 164 162 if (item.canEdit()) { 165 163 const editBtn = header.createEl("button", { 166 - cls: "atmark-item-edit-btn", 164 + cls: "atmosphere-item-edit-btn", 167 165 }); 168 166 setIcon(editBtn, "more-vertical"); 169 167 editBtn.addEventListener("click", (e) => { ··· 176 174 177 175 item.render(el); 178 176 179 - const footer = el.createEl("div", { cls: "atmark-item-footer" }); 177 + const footer = el.createEl("div", { cls: "atmosphere-item-footer" }); 180 178 footer.createEl("span", { 181 179 text: new Date(item.getCreatedAt()).toLocaleDateString(), 182 - cls: "atmark-date", 180 + cls: "atmosphere-date", 183 181 }); 184 182 185 183 // Show note indicator for items with attached notes (semble cards) 186 184 const notes = item.getAttachedNotes?.(); 187 185 if (notes && notes.length > 0) { 188 - const noteIndicator = footer.createEl("div", { cls: "atmark-note-indicator" }); 189 - const icon = noteIndicator.createEl("span", { cls: "atmark-note-icon" }); 186 + const noteIndicator = footer.createEl("div", { cls: "atmosphere-note-indicator" }); 187 + const icon = noteIndicator.createEl("span", { cls: "atmosphere-note-icon" }); 190 188 setIcon(icon, "message-square"); 191 189 noteIndicator.createEl("span", { 192 190 text: `${notes.length} note${notes.length > 1 ? 's' : ''}`, 193 - cls: "atmark-note-count" 191 + cls: "atmosphere-note-count" 194 192 }); 195 193 } 196 194 }
+6 -6
src/views/standardfeed.ts
··· 1 1 import { getSubscribedPublications } from "lib/standardsite"; 2 - import ATmarkPlugin from "main"; 2 + import AtmospherePlugin from "main"; 3 3 import { ItemView, Notice, WorkspaceLeaf, setIcon } from "obsidian"; 4 4 import { Main as Document } from "@atcute/standard-site/types/document"; 5 5 import { Main as Publication } from "@atcute/standard-site/types/publication"; ··· 7 7 import { parseResourceUri } from "@atcute/lexicons"; 8 8 import { getPublicationDocuments } from "lib/standardsite"; 9 9 10 - export const VIEW_STANDARD_FEED = "standard-site-feed"; 10 + export const VIEW_ATMOSPHERE_STANDARD_FEED = "atmosphere-standard-site-feed"; 11 11 12 12 export class StandardFeedView extends ItemView { 13 - plugin: ATmarkPlugin; 13 + plugin: AtmospherePlugin; 14 14 15 - constructor(leaf: WorkspaceLeaf, plugin: ATmarkPlugin) { 15 + constructor(leaf: WorkspaceLeaf, plugin: AtmospherePlugin) { 16 16 super(leaf); 17 17 this.plugin = plugin; 18 18 } 19 19 20 20 getViewType() { 21 - return VIEW_STANDARD_FEED; 21 + return VIEW_ATMOSPHERE_STANDARD_FEED; 22 22 } 23 23 24 24 getDisplayText() { ··· 132 132 133 133 const parsed = parseResourceUri(pub.uri); 134 134 if (!parsed.ok) { 135 - // This is the name of the plugin, which contains the acronym "AT" 135 + // URI is an acronym 136 136 // eslint-disable-next-line obsidianmd/ui/sentence-case 137 137 container.createEl("p", { text: "Failed to parse publication URI." }); 138 138 console.error("Failed to parse publication URI:", parsed.error);
standard.site.png

This is a binary file and will not be displayed.

+152 -152
styles.css
··· 1 - /* ATmark View */ 2 - .atmark-view { 1 + /* Atmosphere View */ 2 + .atmosphere-view { 3 3 padding: 20px; 4 4 } 5 5 6 - .atmark-header { 6 + .atmosphere-header { 7 7 margin-bottom: 24px; 8 8 padding-bottom: 16px; 9 9 border-bottom: 1px solid var(--background-modifier-border); 10 10 } 11 11 12 - .atmark-source-selector { 12 + .atmosphere-source-selector { 13 13 display: flex; 14 14 align-items: center; 15 15 justify-content: center; ··· 19 19 position: relative; 20 20 } 21 21 22 - .atmark-source-option { 22 + .atmosphere-source-option { 23 23 display: flex; 24 24 align-items: center; 25 25 justify-content: center; ··· 34 34 margin-bottom: -1px; 35 35 } 36 36 37 - .atmark-source-option::after { 37 + .atmosphere-source-option::after { 38 38 content: ""; 39 39 position: absolute; 40 40 bottom: 0; ··· 45 45 transition: background 0.15s ease; 46 46 } 47 47 48 - .atmark-source-option:hover { 48 + .atmosphere-source-option:hover { 49 49 background: var(--background-modifier-hover); 50 50 } 51 51 52 - .atmark-source-option:has(input:checked)::after { 52 + .atmosphere-source-option:has(input:checked)::after { 53 53 background: var(--interactive-accent); 54 54 } 55 55 56 - .atmark-source-option:has(input:checked) .atmark-source-text { 56 + .atmosphere-source-option:has(input:checked) .atmosphere-source-text { 57 57 color: var(--interactive-accent); 58 58 font-weight: var(--font-semibold); 59 59 } 60 60 61 - .atmark-source-radio { 61 + .atmosphere-source-radio { 62 62 display: none; 63 63 } 64 64 65 - .atmark-source-text { 65 + .atmosphere-source-text { 66 66 font-size: var(--font-ui-small); 67 67 font-weight: var(--font-medium); 68 68 color: var(--text-muted); 69 69 } 70 70 71 - .atmark-filters { 71 + .atmosphere-filters { 72 72 display: flex; 73 73 flex-direction: column; 74 74 gap: 16px; 75 75 margin-bottom: 16px; 76 76 } 77 77 78 - .atmark-filter-section { 78 + .atmosphere-filter-section { 79 79 display: flex; 80 80 flex-direction: column; 81 81 gap: 6px; 82 82 } 83 83 84 - .atmark-filter-title-row { 84 + .atmosphere-filter-title-row { 85 85 display: flex; 86 86 align-items: center; 87 87 gap: 6px; 88 88 margin-bottom: 2px; 89 89 } 90 90 91 - .atmark-filter-title { 91 + .atmosphere-filter-title { 92 92 margin: 0; 93 93 font-size: var(--font-smallest); 94 94 font-weight: var(--font-semibold); ··· 97 97 letter-spacing: 0.05em; 98 98 } 99 99 100 - .atmark-filter-create-btn { 100 + .atmosphere-filter-create-btn { 101 101 display: flex; 102 102 align-items: center; 103 103 justify-content: center; ··· 113 113 opacity: 0.7; 114 114 } 115 115 116 - .atmark-filter-create-btn:hover { 116 + .atmosphere-filter-create-btn:hover { 117 117 background: var(--background-modifier-hover); 118 118 color: var(--interactive-accent); 119 119 opacity: 1; 120 120 } 121 121 122 - .atmark-filter-create-btn svg { 122 + .atmosphere-filter-create-btn svg { 123 123 width: 12px; 124 124 height: 12px; 125 125 } 126 126 127 - .atmark-filter-chips { 127 + .atmosphere-filter-chips { 128 128 display: flex; 129 129 flex-wrap: wrap; 130 130 gap: 6px; 131 131 align-items: center; 132 132 } 133 133 134 - .atmark-chip { 134 + .atmosphere-chip { 135 135 padding: 3px 10px; 136 136 border-radius: var(--radius-m); 137 137 border: none; ··· 144 144 white-space: nowrap; 145 145 } 146 146 147 - .atmark-chip:hover { 147 + .atmosphere-chip:hover { 148 148 background: var(--background-modifier-border-hover); 149 149 color: var(--text-normal); 150 150 transform: translateY(-1px); 151 151 } 152 152 153 - .atmark-chip-active { 153 + .atmosphere-chip-active { 154 154 background: var(--interactive-accent); 155 155 color: var(--text-on-accent); 156 156 font-weight: var(--font-semibold); 157 157 } 158 158 159 - .atmark-chip-active:hover { 159 + .atmosphere-chip-active:hover { 160 160 background: var(--interactive-accent-hover); 161 161 transform: translateY(-1px); 162 162 } 163 163 164 - .atmark-grid { 164 + .atmosphere-grid { 165 165 display: grid; 166 166 grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); 167 167 gap: 16px; 168 168 padding: 8px 0; 169 169 } 170 170 171 - .atmark-item { 171 + .atmosphere-item { 172 172 background: var(--background-secondary); 173 173 border: 1px solid var(--background-modifier-border); 174 174 border-radius: var(--radius-m); ··· 179 179 cursor: pointer; 180 180 } 181 181 182 - .atmark-item:hover { 182 + .atmosphere-item:hover { 183 183 box-shadow: var(--shadow-s); 184 184 border-color: var(--background-modifier-border-hover); 185 185 } 186 186 187 - .atmark-item-header { 187 + .atmosphere-item-header { 188 188 display: flex; 189 189 justify-content: space-between; 190 190 align-items: flex-start; 191 191 gap: 8px; 192 192 } 193 193 194 - .atmark-item-edit-btn { 194 + .atmosphere-item-edit-btn { 195 195 display: flex; 196 196 align-items: center; 197 197 justify-content: center; ··· 208 208 transition: all 0.15s ease; 209 209 } 210 210 211 - .atmark-item:hover .atmark-item-edit-btn { 211 + .atmosphere-item:hover .atmosphere-item-edit-btn { 212 212 opacity: 1; 213 213 } 214 214 215 - .atmark-item-edit-btn:hover { 215 + .atmosphere-item-edit-btn:hover { 216 216 background: var(--background-modifier-hover); 217 217 color: var(--text-normal); 218 218 opacity: 1; 219 219 } 220 220 221 - .atmark-item-edit-btn svg { 221 + .atmosphere-item-edit-btn svg { 222 222 width: 14px; 223 223 height: 14px; 224 224 } 225 225 226 - .atmark-badge { 226 + .atmosphere-badge { 227 227 font-size: 10px; 228 228 padding: 3px 8px; 229 229 border-radius: 12px; ··· 233 233 letter-spacing: 0.3px; 234 234 } 235 235 236 - .atmark-badge-semble { 236 + .atmosphere-badge-semble { 237 237 background: color-mix(in srgb, var(--color-orange) 15%, transparent); 238 238 color: var(--color-orange); 239 239 border: 1px solid color-mix(in srgb, var(--color-orange) 30%, transparent); 240 240 } 241 241 242 - .atmark-badge-bookmark { 242 + .atmosphere-badge-bookmark { 243 243 background: color-mix(in srgb, var(--color-cyan) 15%, transparent); 244 244 color: var(--color-cyan); 245 245 border: 1px solid color-mix(in srgb, var(--color-cyan) 30%, transparent); 246 246 } 247 247 248 - .atmark-badge-margin { 248 + .atmosphere-badge-margin { 249 249 background: color-mix(in srgb, var(--color-purple) 15%, transparent); 250 250 color: var(--color-purple); 251 251 border: 1px solid color-mix(in srgb, var(--color-purple) 30%, transparent); 252 252 } 253 253 254 - .atmark-item-footer { 254 + .atmosphere-item-footer { 255 255 display: flex; 256 256 justify-content: space-between; 257 257 font-size: var(--font-smallest); ··· 261 261 border-top: 1px solid var(--background-modifier-border); 262 262 } 263 263 264 - .atmark-date { 264 + .atmosphere-date { 265 265 font-size: var(--font-smallest); 266 266 color: var(--text-faint); 267 267 } 268 268 269 - .atmark-error { 269 + .atmosphere-error { 270 270 color: var(--text-error); 271 271 } 272 272 273 273 274 274 /* Item Content (shared between sources) */ 275 - .atmark-item-content { 275 + .atmosphere-item-content { 276 276 display: flex; 277 277 flex-direction: column; 278 278 gap: 12px; 279 279 } 280 280 281 - .atmark-item-title { 281 + .atmosphere-item-title { 282 282 font-weight: var(--font-semibold); 283 283 font-size: 1em; 284 284 color: var(--text-normal); ··· 290 290 margin-bottom: 4px; 291 291 } 292 292 293 - .atmark-item-image { 293 + .atmosphere-item-image { 294 294 width: 100%; 295 295 max-height: 120px; 296 296 object-fit: cover; 297 297 border-radius: var(--radius-s); 298 298 } 299 299 300 - .atmark-item-desc { 300 + .atmosphere-item-desc { 301 301 color: var(--text-muted); 302 302 font-size: var(--font-small); 303 303 margin: 0; ··· 308 308 line-height: 1.5; 309 309 } 310 310 311 - .atmark-item-site { 311 + .atmosphere-item-site { 312 312 font-size: var(--font-smallest); 313 313 color: var(--text-faint); 314 314 margin-bottom: 2px; 315 315 } 316 316 317 - .atmark-item-url { 317 + .atmosphere-item-url { 318 318 font-size: var(--font-smallest); 319 319 color: var(--text-accent); 320 320 text-decoration: none; ··· 325 325 overflow: hidden; 326 326 } 327 327 328 - .atmark-item-url:hover { 328 + .atmosphere-item-url:hover { 329 329 text-decoration: underline; 330 330 } 331 331 332 - .atmark-item-tags { 332 + .atmosphere-item-tags { 333 333 display: flex; 334 334 flex-wrap: wrap; 335 335 gap: 6px; 336 336 margin-bottom: 8px; 337 337 } 338 338 339 - .atmark-tag { 339 + .atmosphere-tag { 340 340 font-size: var(--font-smallest); 341 341 padding: 2px 8px; 342 342 border-radius: var(--radius-s); ··· 345 345 border: 1px solid var(--background-modifier-border-hover); 346 346 } 347 347 348 - .atmark-item-collections { 348 + .atmosphere-item-collections { 349 349 display: flex; 350 350 flex-wrap: wrap; 351 351 gap: 6px; 352 352 margin-bottom: 8px; 353 353 } 354 354 355 - .atmark-collection { 355 + .atmosphere-collection { 356 356 font-size: var(--font-smallest); 357 357 padding: 2px 8px; 358 358 border-radius: var(--radius-s); ··· 361 361 border: 1px solid color-mix(in srgb, var(--color-purple) 30%, transparent); 362 362 } 363 363 364 - .atmark-item-collections-section { 364 + .atmosphere-item-collections-section { 365 365 margin-top: 20px; 366 366 padding-top: 20px; 367 367 border-top: 1px solid var(--background-modifier-border); 368 368 } 369 369 370 - .atmark-item-tags-section { 370 + .atmosphere-item-tags-section { 371 371 margin-top: 20px; 372 372 padding-top: 20px; 373 373 border-top: 1px solid var(--background-modifier-border); 374 374 } 375 375 376 376 /* Note indicator for cards with attached notes */ 377 - .atmark-note-indicator { 377 + .atmosphere-note-indicator { 378 378 display: flex; 379 379 align-items: center; 380 380 gap: 4px; ··· 382 382 color: var(--text-muted); 383 383 } 384 384 385 - .atmark-note-icon { 385 + .atmosphere-note-icon { 386 386 display: flex; 387 387 align-items: center; 388 388 color: var(--text-muted); 389 389 } 390 390 391 - .atmark-note-icon svg { 391 + .atmosphere-note-icon svg { 392 392 width: 12px; 393 393 height: 12px; 394 394 } 395 395 396 - .atmark-note-count { 396 + .atmosphere-note-count { 397 397 font-size: var(--font-smallest); 398 398 } 399 399 400 400 /* Detail Modal (shared between sources) */ 401 - .atmark-detail-body { 401 + .atmosphere-detail-body { 402 402 display: flex; 403 403 flex-direction: column; 404 404 gap: 16px; 405 405 } 406 406 407 - .atmark-detail-title { 407 + .atmosphere-detail-title { 408 408 margin: 0; 409 409 font-size: var(--h2-size); 410 410 font-weight: var(--font-semibold); ··· 412 412 line-height: 1.3; 413 413 } 414 414 415 - .atmark-detail-image { 415 + .atmosphere-detail-image { 416 416 max-width: 100%; 417 417 max-height: 200px; 418 418 object-fit: contain; 419 419 border-radius: var(--radius-m); 420 420 } 421 421 422 - .atmark-detail-description { 422 + .atmosphere-detail-description { 423 423 margin: 0; 424 424 color: var(--text-normal); 425 425 line-height: var(--line-height-normal); 426 426 } 427 427 428 - .atmark-detail-meta { 428 + .atmosphere-detail-meta { 429 429 display: grid; 430 430 grid-template-columns: repeat(2, 1fr); 431 431 gap: 12px; ··· 434 434 border-radius: var(--radius-m); 435 435 } 436 436 437 - .atmark-detail-meta-item { 437 + .atmosphere-detail-meta-item { 438 438 display: flex; 439 439 flex-direction: column; 440 440 gap: 2px; 441 441 } 442 442 443 - .atmark-detail-meta-label { 443 + .atmosphere-detail-meta-label { 444 444 font-size: var(--font-smallest); 445 445 color: var(--text-faint); 446 446 text-transform: uppercase; 447 447 letter-spacing: 0.5px; 448 448 } 449 449 450 - .atmark-detail-meta-value { 450 + .atmosphere-detail-meta-value { 451 451 font-size: var(--font-small); 452 452 color: var(--text-normal); 453 453 } 454 454 455 - .atmark-detail-link-wrapper { 455 + .atmosphere-detail-link-wrapper { 456 456 padding-top: 8px; 457 457 } 458 458 459 - .atmark-detail-link { 459 + .atmosphere-detail-link { 460 460 font-size: var(--font-small); 461 461 color: var(--text-accent); 462 462 text-decoration: none; 463 463 word-break: break-all; 464 464 } 465 465 466 - .atmark-detail-link:hover { 466 + .atmosphere-detail-link:hover { 467 467 text-decoration: underline; 468 468 } 469 469 470 - .atmark-detail-section-title { 470 + .atmosphere-detail-section-title { 471 471 margin: 0 0 12px 0; 472 472 font-size: var(--font-small); 473 473 font-weight: var(--font-semibold); ··· 477 477 } 478 478 479 479 /* Modals and Forms (shared) */ 480 - .atmark-modal { 480 + .atmosphere-modal { 481 481 padding: 16px; 482 482 } 483 483 484 - .atmark-modal h2 { 484 + .atmosphere-modal h2 { 485 485 margin: 0 0 16px 0; 486 486 font-size: var(--h2-size); 487 487 font-weight: var(--font-semibold); 488 488 color: var(--text-normal); 489 489 } 490 490 491 - .atmark-form { 491 + .atmosphere-form { 492 492 display: flex; 493 493 flex-direction: column; 494 494 gap: 16px; 495 495 } 496 496 497 - .atmark-form-group { 497 + .atmosphere-form-group { 498 498 display: flex; 499 499 flex-direction: column; 500 500 gap: 6px; 501 501 } 502 502 503 - .atmark-form-group label { 503 + .atmosphere-form-group label { 504 504 font-size: var(--font-small); 505 505 font-weight: var(--font-medium); 506 506 color: var(--text-normal); 507 507 } 508 508 509 - .atmark-input, 510 - .atmark-textarea { 509 + .atmosphere-input, 510 + .atmosphere-textarea { 511 511 padding: 8px 12px; 512 512 background: var(--background-primary); 513 513 border: 1px solid var(--background-modifier-border); ··· 518 518 transition: border-color 0.15s ease; 519 519 } 520 520 521 - .atmark-input:focus, 522 - .atmark-textarea:focus { 521 + .atmosphere-input:focus, 522 + .atmosphere-textarea:focus { 523 523 outline: none; 524 524 border-color: var(--interactive-accent); 525 525 box-shadow: 0 0 0 2px var(--background-modifier-border-focus); 526 526 } 527 527 528 - .atmark-input::placeholder, 529 - .atmark-textarea::placeholder { 528 + .atmosphere-input::placeholder, 529 + .atmosphere-textarea::placeholder { 530 530 color: var(--text-faint); 531 531 } 532 532 533 - .atmark-textarea { 533 + .atmosphere-textarea { 534 534 resize: vertical; 535 535 min-height: 60px; 536 536 } 537 537 538 - .atmark-modal-actions { 538 + .atmosphere-modal-actions { 539 539 display: flex; 540 540 align-items: center; 541 541 gap: 8px; ··· 543 543 border-top: 1px solid var(--background-modifier-border); 544 544 } 545 545 546 - .atmark-spacer { 546 + .atmosphere-spacer { 547 547 flex: 1; 548 548 } 549 549 550 - .atmark-btn { 550 + .atmosphere-btn { 551 551 padding: 8px 16px; 552 552 border-radius: var(--radius-s); 553 553 font-size: var(--font-small); ··· 556 556 transition: all 0.15s ease; 557 557 } 558 558 559 - .atmark-btn:disabled { 559 + .atmosphere-btn:disabled { 560 560 opacity: 0.5; 561 561 cursor: not-allowed; 562 562 } 563 563 564 - .atmark-btn-secondary { 564 + .atmosphere-btn-secondary { 565 565 background: var(--background-secondary); 566 566 border: 1px solid var(--background-modifier-border); 567 567 color: var(--text-normal); 568 568 } 569 569 570 - .atmark-btn-secondary:hover:not(:disabled) { 570 + .atmosphere-btn-secondary:hover:not(:disabled) { 571 571 background: var(--background-modifier-hover); 572 572 } 573 573 574 - .atmark-btn-primary { 574 + .atmosphere-btn-primary { 575 575 background: var(--interactive-accent); 576 576 border: 1px solid var(--interactive-accent); 577 577 color: var(--text-on-accent); 578 578 } 579 579 580 - .atmark-btn-primary:hover:not(:disabled) { 580 + .atmosphere-btn-primary:hover:not(:disabled) { 581 581 background: var(--interactive-accent-hover); 582 582 } 583 583 584 - .atmark-btn-danger { 584 + .atmosphere-btn-danger { 585 585 background: color-mix(in srgb, var(--color-red) 15%, transparent); 586 586 border: none; 587 587 color: var(--color-red); 588 588 } 589 589 590 - .atmark-btn-danger:hover:not(:disabled) { 590 + .atmosphere-btn-danger:hover:not(:disabled) { 591 591 background: color-mix(in srgb, var(--color-red) 25%, transparent); 592 592 } 593 593 594 - .atmark-warning-text { 594 + .atmosphere-warning-text { 595 595 color: var(--text-muted); 596 596 margin-bottom: 16px; 597 597 } 598 598 599 - .atmark-tags-container { 599 + .atmosphere-tags-container { 600 600 display: flex; 601 601 flex-direction: column; 602 602 gap: 8px; 603 603 margin-bottom: 8px; 604 604 } 605 605 606 - .atmark-tag-row { 606 + .atmosphere-tag-row { 607 607 display: flex; 608 608 align-items: center; 609 609 gap: 8px; 610 610 } 611 611 612 - .atmark-tag-row .atmark-input { 612 + .atmosphere-tag-row .atmosphere-input { 613 613 flex: 1; 614 614 } 615 615 616 - .atmark-tag-remove-btn { 616 + .atmosphere-tag-remove-btn { 617 617 width: 32px; 618 618 height: 32px; 619 619 padding: 0; ··· 623 623 } 624 624 625 625 626 - .atmark-collection-list { 626 + .atmosphere-collection-list { 627 627 display: flex; 628 628 flex-direction: column; 629 629 gap: 8px; ··· 631 631 overflow-y: auto; 632 632 } 633 633 634 - .atmark-collection-item { 634 + .atmosphere-collection-item { 635 635 display: flex; 636 636 align-items: center; 637 637 gap: 12px; ··· 643 643 transition: all 0.15s ease; 644 644 } 645 645 646 - .atmark-collection-item:hover { 646 + .atmosphere-collection-item:hover { 647 647 background: var(--background-modifier-hover); 648 648 border-color: var(--background-modifier-border-hover); 649 649 } 650 650 651 - .atmark-collection-checkbox { 651 + .atmosphere-collection-checkbox { 652 652 width: 18px; 653 653 height: 18px; 654 654 margin: 0; ··· 656 656 accent-color: var(--interactive-accent); 657 657 } 658 658 659 - .atmark-collection-item-info { 659 + .atmosphere-collection-item-info { 660 660 display: flex; 661 661 flex-direction: column; 662 662 gap: 2px; 663 663 flex: 1; 664 664 } 665 665 666 - .atmark-collection-item-name { 666 + .atmosphere-collection-item-name { 667 667 font-weight: var(--font-medium); 668 668 color: var(--text-normal); 669 669 } 670 670 671 - .atmark-collection-item-desc { 671 + .atmosphere-collection-item-desc { 672 672 font-size: var(--font-small); 673 673 color: var(--text-muted); 674 674 } 675 675 676 - .atmark-tag-list { 676 + .atmosphere-tag-list { 677 677 display: flex; 678 678 flex-wrap: wrap; 679 679 gap: 6px; 680 680 margin-bottom: 8px; 681 681 } 682 682 683 - .atmark-tag-item { 683 + .atmosphere-tag-item { 684 684 display: flex; 685 685 align-items: center; 686 686 padding: 4px 12px; ··· 692 692 color: var(--text-muted); 693 693 } 694 694 695 - .atmark-tag-item:hover { 695 + .atmosphere-tag-item:hover { 696 696 background: var(--background-modifier-border-hover); 697 697 color: var(--text-normal); 698 698 } 699 699 700 - .atmark-tag-item:has(input:checked) { 700 + .atmosphere-tag-item:has(input:checked) { 701 701 background: var(--interactive-accent); 702 702 color: var(--text-on-accent); 703 703 } 704 704 705 - .atmark-tag-item input { 705 + .atmosphere-tag-item input { 706 706 display: none; 707 707 } 708 708 709 709 /* Semble-specific styles (for NOTE cards and attached notes) */ 710 - .atmark-semble-card-note { 710 + .atmosphere-semble-card-note { 711 711 margin: 0; 712 712 padding: 8px 12px; 713 713 background: var(--background-primary); ··· 720 720 line-height: var(--line-height-normal); 721 721 } 722 722 723 - .atmark-semble-card-text { 723 + .atmosphere-semble-card-text { 724 724 margin: 0; 725 725 line-height: 1.5; 726 726 color: var(--text-normal); ··· 730 730 overflow: hidden; 731 731 } 732 732 733 - .atmark-semble-detail-text { 733 + .atmosphere-semble-detail-text { 734 734 margin: 0; 735 735 white-space: pre-wrap; 736 736 line-height: var(--line-height-normal); ··· 738 738 font-size: 1.1em; 739 739 } 740 740 741 - .atmark-semble-detail-notes-section { 741 + .atmosphere-semble-detail-notes-section { 742 742 margin-top: 20px; 743 743 padding-top: 20px; 744 744 border-top: 1px solid var(--background-modifier-border); 745 745 } 746 746 747 - .atmark-semble-detail-note { 747 + .atmosphere-semble-detail-note { 748 748 display: flex; 749 749 align-items: flex-start; 750 750 justify-content: space-between; ··· 756 756 margin-bottom: 8px; 757 757 } 758 758 759 - .atmark-semble-detail-note-content { 759 + .atmosphere-semble-detail-note-content { 760 760 display: flex; 761 761 gap: 12px; 762 762 flex: 1; 763 763 min-width: 0; 764 764 } 765 765 766 - .atmark-semble-detail-note-icon { 766 + .atmosphere-semble-detail-note-icon { 767 767 flex-shrink: 0; 768 768 color: var(--color-accent); 769 769 } 770 770 771 - .atmark-semble-detail-note-icon svg { 771 + .atmosphere-semble-detail-note-icon svg { 772 772 width: 16px; 773 773 height: 16px; 774 774 } 775 775 776 - .atmark-semble-detail-note-text { 776 + .atmosphere-semble-detail-note-text { 777 777 margin: 0; 778 778 color: var(--text-normal); 779 779 line-height: var(--line-height-normal); ··· 781 781 } 782 782 783 783 /* Card type badges */ 784 - .atmark-semble-badge-note { 784 + .atmosphere-semble-badge-note { 785 785 background: var(--color-accent); 786 786 color: var(--text-on-accent); 787 787 } 788 788 789 - .atmark-semble-badge-url { 789 + .atmosphere-semble-badge-url { 790 790 background: color-mix(in srgb, var(--color-purple) 80%, var(--background-primary)); 791 791 color: var(--text-on-accent); 792 792 } 793 793 794 - .atmark-badge-source { 794 + .atmosphere-badge-source { 795 795 font-size: var(--font-smallest); 796 796 opacity: 0.8; 797 797 } 798 798 799 - .atmark-semble-badge-semble { 799 + .atmosphere-semble-badge-semble { 800 800 background: color-mix(in srgb, var(--color-green) 80%, var(--background-primary)); 801 801 color: var(--text-on-accent); 802 802 } 803 803 804 804 /* Profile Icon */ 805 - .atmark-profile-icon { 805 + .atmosphere-profile-icon { 806 806 display: flex; 807 807 align-items: center; 808 808 gap: 6px; ··· 815 815 transition: background 0.15s ease; 816 816 } 817 817 818 - .atmark-profile-icon:hover { 818 + .atmosphere-profile-icon:hover { 819 819 background: var(--background-modifier-hover); 820 820 } 821 821 822 - .atmark-avatar-btn { 822 + .atmosphere-avatar-btn { 823 823 display: flex; 824 824 align-items: center; 825 825 justify-content: center; ··· 834 834 transition: opacity 0.15s ease; 835 835 } 836 836 837 - .atmark-avatar-btn:hover { 837 + .atmosphere-avatar-btn:hover { 838 838 opacity: 0.8; 839 839 } 840 840 841 - .atmark-avatar-img { 841 + .atmosphere-avatar-img { 842 842 width: 100%; 843 843 height: 100%; 844 844 object-fit: cover; 845 845 border-radius: 50%; 846 846 } 847 847 848 - .atmark-avatar-initials { 848 + .atmosphere-avatar-initials { 849 849 font-size: var(--font-smallest); 850 850 font-weight: var(--font-semibold); 851 851 color: var(--text-muted); 852 852 } 853 853 854 - .atmark-avatar-placeholder { 854 + .atmosphere-avatar-placeholder { 855 855 display: flex; 856 856 align-items: center; 857 857 justify-content: center; ··· 864 864 font-size: var(--font-smallest); 865 865 } 866 866 867 - .atmark-profile-info { 867 + .atmosphere-profile-info { 868 868 display: flex; 869 869 flex-direction: column; 870 870 align-items: flex-end; 871 871 gap: 1px; 872 872 } 873 873 874 - .atmark-profile-name { 874 + .atmosphere-profile-name { 875 875 font-size: var(--font-ui-small); 876 876 font-weight: var(--font-medium); 877 877 color: var(--text-muted); 878 878 line-height: 1.2; 879 879 } 880 880 881 - .atmark-profile-handle { 881 + .atmosphere-profile-handle { 882 882 font-size: var(--font-smallest); 883 883 color: var(--text-faint); 884 884 line-height: 1.2; 885 885 } 886 886 887 887 /* Generic Card Detail Modal (used for all sources) */ 888 - .atmark-detail-modal { 888 + .atmosphere-detail-modal { 889 889 padding: 20px; 890 890 max-width: 600px; 891 891 } 892 892 893 - .atmark-detail-header { 893 + .atmosphere-detail-header { 894 894 margin-bottom: 16px; 895 895 } 896 896 897 - .atmark-detail-footer { 897 + .atmosphere-detail-footer { 898 898 margin-top: 20px; 899 899 padding-top: 16px; 900 900 border-top: 1px solid var(--background-modifier-border); 901 901 } 902 902 903 - .atmark-detail-date { 903 + .atmosphere-detail-date { 904 904 font-size: var(--font-small); 905 905 color: var(--text-faint); 906 906 } 907 907 908 908 /* Semble-specific Add Note Form */ 909 - .atmark-semble-detail-add-note { 909 + .atmosphere-semble-detail-add-note { 910 910 margin-top: 20px; 911 911 padding-top: 20px; 912 912 border-top: 1px solid var(--background-modifier-border); 913 913 } 914 914 915 - .atmark-semble-add-note-form { 915 + .atmosphere-semble-add-note-form { 916 916 display: flex; 917 917 flex-direction: column; 918 918 gap: 12px; 919 919 } 920 920 921 - .atmark-semble-note-input { 921 + .atmosphere-semble-note-input { 922 922 min-height: 80px; 923 923 resize: vertical; 924 924 } 925 925 926 - .atmark-semble-note-delete-btn { 926 + .atmosphere-semble-note-delete-btn { 927 927 display: flex; 928 928 align-items: center; 929 929 justify-content: center; ··· 940 940 transition: all 0.15s ease; 941 941 } 942 942 943 - .atmark-semble-note-delete-btn:hover { 943 + .atmosphere-semble-note-delete-btn:hover { 944 944 background: color-mix(in srgb, var(--color-red) 15%, transparent); 945 945 color: var(--color-red); 946 946 opacity: 1; 947 947 } 948 948 949 - .atmark-semble-note-delete-btn svg { 949 + .atmosphere-semble-note-delete-btn svg { 950 950 width: 14px; 951 951 height: 14px; 952 952 } 953 953 954 954 /* Responsive styles */ 955 955 @media (max-width: 600px) { 956 - .atmark-view { 956 + .atmosphere-view { 957 957 padding: 12px; 958 958 } 959 959 960 - .atmark-header { 960 + .atmosphere-header { 961 961 margin-bottom: 16px; 962 962 padding-bottom: 12px; 963 963 } 964 964 965 - .atmark-profile-icon { 965 + .atmosphere-profile-icon { 966 966 display: none; 967 967 } 968 968 969 - .atmark-source-option { 969 + .atmosphere-source-option { 970 970 padding: 8px 16px; 971 971 font-size: var(--font-ui-small); 972 972 } 973 973 974 - .atmark-source-text { 974 + .atmosphere-source-text { 975 975 font-size: var(--font-ui-small); 976 976 } 977 977 978 - .atmark-source-selector { 978 + .atmosphere-source-selector { 979 979 justify-content: center; 980 980 } 981 981 982 - .atmark-grid { 982 + .atmosphere-grid { 983 983 grid-template-columns: 1fr; 984 984 gap: 12px; 985 985 } ··· 989 989 gap: 12px; 990 990 } 991 991 992 - .atmark-filter-section { 992 + .atmosphere-filter-section { 993 993 margin-bottom: 12px; 994 994 } 995 995 } 996 996 997 997 /* Hide profile in narrow sidebar widths (but not mobile) */ 998 998 @media (max-width: 400px) { 999 - .atmark-profile-icon { 999 + .atmosphere-profile-icon { 1000 1000 display: none; 1001 1001 } 1002 1002 } 1003 1003 1004 - .is-mobile .atmark-profile-icon { 1004 + .is-mobile .atmosphere-profile-icon { 1005 1005 display: none; 1006 1006 } 1007 1007 1008 - .is-mobile .atmark-source-option { 1008 + .is-mobile .atmosphere-source-option { 1009 1009 padding: 8px 16px; 1010 1010 font-size: var(--font-ui-small); 1011 1011 } 1012 1012 1013 - .is-mobile .atmark-source-text { 1013 + .is-mobile .atmosphere-source-text { 1014 1014 font-size: var(--font-ui-small); 1015 1015 } 1016 1016 1017 - .is-mobile .atmark-source-selector { 1017 + .is-mobile .atmosphere-source-selector { 1018 1018 justify-content: center; 1019 1019 } 1020 1020