···11+# vscode
22+.vscode
33+44+# Intellij
55+*.iml
66+.idea
77+88+# npm
99+node_modules
1010+1111+# Don't include the compiled main.js file in the repo.
1212+# They should be uploaded to GitHub releases instead.
1313+main.js
1414+1515+# Exclude sourcemaps
1616+*.map
1717+1818+# obsidian
1919+data.json
2020+2121+# Exclude macOS Finder (System Explorer) View States
2222+.DS_Store
···11+# Obsidian community plugin
22+33+## Project overview
44+55+- Target: Obsidian Community Plugin (TypeScript โ bundled JavaScript).
66+- Entry point: `main.ts` compiled to `main.js` and loaded by Obsidian.
77+- Required release artifacts: `main.js`, `manifest.json`, and optional `styles.css`.
88+99+## Environment & tooling
1010+1111+- Node.js: use current LTS (Node 18+ recommended).
1212+- **Package manager: npm** (required for this sample - `package.json` defines npm scripts and dependencies).
1313+- **Bundler: esbuild** (required for this sample - `esbuild.config.mjs` and build scripts depend on it). Alternative bundlers like Rollup or webpack are acceptable for other projects if they bundle all external dependencies into `main.js`.
1414+- Types: `obsidian` type definitions.
1515+1616+**Note**: This sample project has specific technical dependencies on npm and esbuild. If you're creating a plugin from scratch, you can choose different tools, but you'll need to replace the build configuration accordingly.
1717+1818+### Install
1919+2020+```bash
2121+npm install
2222+```
2323+2424+### Dev (watch)
2525+2626+```bash
2727+npm run dev
2828+```
2929+3030+### Production build
3131+3232+```bash
3333+npm run build
3434+```
3535+3636+## Linting
3737+3838+- To use eslint install eslint from terminal: `npm install -g eslint`
3939+- To use eslint to analyze this project use this command: `eslint main.ts`
4040+- eslint will then create a report with suggestions for code improvement by file and line number.
4141+- If your source code is in a folder, such as `src`, you can use eslint with this command to analyze all files in that folder: `eslint ./src/`
4242+4343+## File & folder conventions
4444+4545+- **Organize code into multiple files**: Split functionality across separate modules rather than putting everything in `main.ts`.
4646+- Source lives in `src/`. Keep `main.ts` small and focused on plugin lifecycle (loading, unloading, registering commands).
4747+- **Example file structure**:
4848+ ```
4949+ src/
5050+ main.ts # Plugin entry point, lifecycle management
5151+ settings.ts # Settings interface and defaults
5252+ commands/ # Command implementations
5353+ command1.ts
5454+ command2.ts
5555+ ui/ # UI components, modals, views
5656+ modal.ts
5757+ view.ts
5858+ utils/ # Utility functions, helpers
5959+ helpers.ts
6060+ constants.ts
6161+ types.ts # TypeScript interfaces and types
6262+ ```
6363+- **Do not commit build artifacts**: Never commit `node_modules/`, `main.js`, or other generated files to version control.
6464+- Keep the plugin small. Avoid large dependencies. Prefer browser-compatible packages.
6565+- Generated output should be placed at the plugin root or `dist/` depending on your build setup. Release artifacts must end up at the top level of the plugin folder in the vault (`main.js`, `manifest.json`, `styles.css`).
6666+6767+## Manifest rules (`manifest.json`)
6868+6969+- Must include (non-exhaustive):
7070+ - `id` (plugin ID; for local dev it should match the folder name)
7171+ - `name`
7272+ - `version` (Semantic Versioning `x.y.z`)
7373+ - `minAppVersion`
7474+ - `description`
7575+ - `isDesktopOnly` (boolean)
7676+ - Optional: `author`, `authorUrl`, `fundingUrl` (string or map)
7777+- Never change `id` after release. Treat it as stable API.
7878+- Keep `minAppVersion` accurate when using newer APIs.
7979+- Canonical requirements are coded here: https://github.com/obsidianmd/obsidian-releases/blob/master/.github/workflows/validate-plugin-entry.yml
8080+8181+## Testing
8282+8383+- Manual install for testing: copy `main.js`, `manifest.json`, `styles.css` (if any) to:
8484+ ```
8585+ <Vault>/.obsidian/plugins/<plugin-id>/
8686+ ```
8787+- Reload Obsidian and enable the plugin in **Settings โ Community plugins**.
8888+8989+## Commands & settings
9090+9191+- Any user-facing commands should be added via `this.addCommand(...)`.
9292+- If the plugin has configuration, provide a settings tab and sensible defaults.
9393+- Persist settings using `this.loadData()` / `this.saveData()`.
9494+- Use stable command IDs; avoid renaming once released.
9595+9696+## Versioning & releases
9797+9898+- Bump `version` in `manifest.json` (SemVer) and update `versions.json` to map plugin version โ minimum app version.
9999+- Create a GitHub release whose tag exactly matches `manifest.json`'s `version`. Do not use a leading `v`.
100100+- Attach `manifest.json`, `main.js`, and `styles.css` (if present) to the release as individual assets.
101101+- After the initial release, follow the process to add/update your plugin in the community catalog as required.
102102+103103+## Security, privacy, and compliance
104104+105105+Follow Obsidian's **Developer Policies** and **Plugin Guidelines**. In particular:
106106+107107+- Default to local/offline operation. Only make network requests when essential to the feature.
108108+- No hidden telemetry. If you collect optional analytics or call third-party services, require explicit opt-in and document clearly in `README.md` and in settings.
109109+- Never execute remote code, fetch and eval scripts, or auto-update plugin code outside of normal releases.
110110+- Minimize scope: read/write only what's necessary inside the vault. Do not access files outside the vault.
111111+- Clearly disclose any external services used, data sent, and risks.
112112+- Respect user privacy. Do not collect vault contents, filenames, or personal information unless absolutely necessary and explicitly consented.
113113+- Avoid deceptive patterns, ads, or spammy notifications.
114114+- Register and clean up all DOM, app, and interval listeners using the provided `register*` helpers so the plugin unloads safely.
115115+116116+## UX & copy guidelines (for UI text, commands, settings)
117117+118118+- Prefer sentence case for headings, buttons, and titles.
119119+- Use clear, action-oriented imperatives in step-by-step copy.
120120+- Use **bold** to indicate literal UI labels. Prefer "select" for interactions.
121121+- Use arrow notation for navigation: **Settings โ Community plugins**.
122122+- Keep in-app strings short, consistent, and free of jargon.
123123+124124+## Performance
125125+126126+- Keep startup light. Defer heavy work until needed.
127127+- Avoid long-running tasks during `onload`; use lazy initialization.
128128+- Batch disk access and avoid excessive vault scans.
129129+- Debounce/throttle expensive operations in response to file system events.
130130+131131+## Coding conventions
132132+133133+- TypeScript with `"strict": true` preferred.
134134+- **Keep `main.ts` minimal**: Focus only on plugin lifecycle (onload, onunload, addCommand calls). Delegate all feature logic to separate modules.
135135+- **Split large files**: If any file exceeds ~200-300 lines, consider breaking it into smaller, focused modules.
136136+- **Use clear module boundaries**: Each file should have a single, well-defined responsibility.
137137+- Bundle everything into `main.js` (no unbundled runtime deps).
138138+- Avoid Node/Electron APIs if you want mobile compatibility; set `isDesktopOnly` accordingly.
139139+- Prefer `async/await` over promise chains; handle errors gracefully.
140140+141141+## Mobile
142142+143143+- Where feasible, test on iOS and Android.
144144+- Don't assume desktop-only behavior unless `isDesktopOnly` is `true`.
145145+- Avoid large in-memory structures; be mindful of memory and storage constraints.
146146+147147+## Agent do/don't
148148+149149+**Do**
150150+- Add commands with stable IDs (don't rename once released).
151151+- Provide defaults and validation in settings.
152152+- Write idempotent code paths so reload/unload doesn't leak listeners or intervals.
153153+- Use `this.register*` helpers for everything that needs cleanup.
154154+155155+**Don't**
156156+- Introduce network calls without an obvious user-facing reason and documentation.
157157+- Ship features that require cloud services without clear disclosure and explicit opt-in.
158158+- Store or transmit vault contents unless essential and consented.
159159+160160+## Common tasks
161161+162162+### Organize code across multiple files
163163+164164+**main.ts** (minimal, lifecycle only):
165165+```ts
166166+import { Plugin } from "obsidian";
167167+import { MySettings, DEFAULT_SETTINGS } from "./settings";
168168+import { registerCommands } from "./commands";
169169+170170+export default class MyPlugin extends Plugin {
171171+ settings: MySettings;
172172+173173+ async onload() {
174174+ this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
175175+ registerCommands(this);
176176+ }
177177+}
178178+```
179179+180180+**settings.ts**:
181181+```ts
182182+export interface MySettings {
183183+ enabled: boolean;
184184+ apiKey: string;
185185+}
186186+187187+export const DEFAULT_SETTINGS: MySettings = {
188188+ enabled: true,
189189+ apiKey: "",
190190+};
191191+```
192192+193193+**commands/index.ts**:
194194+```ts
195195+import { Plugin } from "obsidian";
196196+import { doSomething } from "./my-command";
197197+198198+export function registerCommands(plugin: Plugin) {
199199+ plugin.addCommand({
200200+ id: "do-something",
201201+ name: "Do something",
202202+ callback: () => doSomething(plugin),
203203+ });
204204+}
205205+```
206206+207207+### Add a command
208208+209209+```ts
210210+this.addCommand({
211211+ id: "your-command-id",
212212+ name: "Do the thing",
213213+ callback: () => this.doTheThing(),
214214+});
215215+```
216216+217217+### Persist settings
218218+219219+```ts
220220+interface MySettings { enabled: boolean }
221221+const DEFAULT_SETTINGS: MySettings = { enabled: true };
222222+223223+async onload() {
224224+ this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
225225+ await this.saveData(this.settings);
226226+}
227227+```
228228+229229+### Register listeners safely
230230+231231+```ts
232232+this.registerEvent(this.app.workspace.on("file-open", f => { /* ... */ }));
233233+this.registerDomEvent(window, "resize", () => { /* ... */ });
234234+this.registerInterval(window.setInterval(() => { /* ... */ }, 1000));
235235+```
236236+237237+## Troubleshooting
238238+239239+- Plugin doesn't load after build: ensure `main.js` and `manifest.json` are at the top level of the plugin folder under `<Vault>/.obsidian/plugins/<plugin-id>/`.
240240+- Build issues: if `main.js` is missing, run `npm run build` or `npm run dev` to compile your TypeScript source code.
241241+- Commands not appearing: verify `addCommand` runs after `onload` and IDs are unique.
242242+- Settings not persisting: ensure `loadData`/`saveData` are awaited and you re-render the UI after changes.
243243+- Mobile-only issues: confirm you're not using desktop-only APIs; check `isDesktopOnly` and adjust.
244244+245245+## References
246246+247247+- Obsidian sample plugin: https://github.com/obsidianmd/obsidian-sample-plugin
248248+- API documentation: https://docs.obsidian.md
249249+- Developer policies: https://docs.obsidian.md/Developer+policies
250250+- Plugin guidelines: https://docs.obsidian.md/Plugins/Releasing/Plugin+guidelines
251251+- Style guide: https://help.obsidian.md/style-guide
+21
LICENSE
···11+MIT License
22+33+Copyright (c) 2025 treethought
44+55+Permission is hereby granted, free of charge, to any person obtaining a copy
66+of this software and associated documentation files (the "Software"), to deal
77+in the Software without restriction, including without limitation the rights
88+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
99+copies of the Software, and to permit persons to whom the Software is
1010+furnished to do so, subject to the following conditions:
1111+1212+The above copyright notice and this permission notice shall be included in all
1313+copies or substantial portions of the Software.
1414+1515+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1616+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1717+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1818+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1919+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2020+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2121+SOFTWARE.
+40-2
README.md
···11-# ATmark (archived)
11+# ATmark
2233Obsidian plugin for AT Protocol bookmarking platforms.
4455-This project has been renamed and moved to [obsidian-atmosphere](https://tangled.org/treethought.xyz/obsidian-atmosphere)
55+66+## Supported platforms
77+88+- **Semble** (`network.cosmik.*`) - Collections and cards
99+- **Bookmarks** (`community.lexicon.bookmarks.*`) - Community bookmarks lexicon with tag filtering (supports kipclip tags)
1010+- **margin.at** (`at.margin.*`) - Bookmarks with collections and tags support
1111+1212+
1313+
1414+1515+## Installation
1616+1717+Install via [BRAT](https://github.com/TfTHacker/obsidian42-brat):
1818+1919+1. Install the BRAT plugin from Community Plugins
2020+2. Open BRAT settings
2121+3. Click "Add Beta plugin"
2222+4. Enter the GitHub URL: `https://github.com/treethought/obsidian-atmark`
2323+5. Enable the plugin in Community Plugins
2424+2525+## Getting Started
2626+2727+### Authentication
2828+2929+1. Open Settings > ATmark
3030+2. Enter your AT Protocol handle or DID
3131+3. Create an app password in your AT Protocol client (Bluesky: Settings > Privacy and security > App passwords)
3232+4. Enter the app password in the plugin settings
3333+5. Save settings
3434+3535+The plugin will automatically connect using your credentials.
3636+3737+### Opening the View
3838+3939+Open the command palette (Ctrl/Cmd + P) and search for "ATmark: Open view". The view will show your bookmarks from all supported platforms.
4040+4141+## Network Use
4242+4343+This plugin connects to AT Protocol services to fetch and manage your bookmarks.
···11+{
22+ "lexicon": 1,
33+ "id": "network.cosmik.card",
44+ "description": "A single record type for all cards.",
55+ "defs": {
66+ "main": {
77+ "type": "record",
88+ "description": "A record representing a card with content.",
99+ "key": "tid",
1010+ "record": {
1111+ "type": "object",
1212+ "required": ["type", "content"],
1313+ "properties": {
1414+ "type": {
1515+ "type": "string",
1616+ "description": "The type of card",
1717+ "knownValues": ["URL", "NOTE"]
1818+ },
1919+ "content": {
2020+ "type": "union",
2121+ "description": "The specific content of the card, determined by the card type.",
2222+ "refs": ["#urlContent", "#noteContent"]
2323+ },
2424+ "url": {
2525+ "type": "string",
2626+ "format": "uri",
2727+ "description": "Optional URL associated with the card. Required for URL cards, optional for NOTE cards."
2828+ },
2929+ "parentCard": {
3030+ "type": "ref",
3131+ "description": "Optional strong reference to a parent card (for NOTE cards).",
3232+ "ref": "com.atproto.repo.strongRef"
3333+ },
3434+ "createdAt": {
3535+ "type": "string",
3636+ "format": "datetime",
3737+ "description": "Timestamp when this card was created (usually set by PDS)."
3838+ },
3939+ "originalCard": {
4040+ "type": "ref",
4141+ "description": "Optional strong reference to the original card (for NOTE cards).",
4242+ "ref": "com.atproto.repo.strongRef"
4343+ },
4444+ "provenance": {
4545+ "type": "ref",
4646+ "description": "Optional provenance information for this card.",
4747+ "ref": "network.cosmik.defs#provenance"
4848+ }
4949+ }
5050+ }
5151+ },
5252+ "urlContent": {
5353+ "type": "object",
5454+ "description": "Content structure for a URL card.",
5555+ "required": ["url"],
5656+ "properties": {
5757+ "url": {
5858+ "type": "string",
5959+ "format": "uri",
6060+ "description": "The URL being saved"
6161+ },
6262+ "metadata": {
6363+ "type": "ref",
6464+ "ref": "#urlMetadata",
6565+ "description": "Optional metadata about the URL"
6666+ }
6767+ }
6868+ },
6969+ "noteContent": {
7070+ "type": "object",
7171+ "description": "Content structure for a note card.",
7272+ "required": ["text"],
7373+ "properties": {
7474+ "text": {
7575+ "type": "string",
7676+ "description": "The note text content",
7777+ "maxLength": 10000
7878+ }
7979+ }
8080+ },
8181+ "urlMetadata": {
8282+ "type": "object",
8383+ "description": "Metadata about a URL.",
8484+ "properties": {
8585+ "title": {
8686+ "type": "string",
8787+ "description": "Title of the page"
8888+ },
8989+ "description": {
9090+ "type": "string",
9191+ "description": "Description of the page"
9292+ },
9393+ "author": {
9494+ "type": "string",
9595+ "description": "Author of the content"
9696+ },
9797+ "publishedDate": {
9898+ "type": "string",
9999+ "format": "datetime",
100100+ "description": "When the content was published"
101101+ },
102102+ "siteName": {
103103+ "type": "string",
104104+ "description": "Name of the site"
105105+ },
106106+ "imageUrl": {
107107+ "type": "string",
108108+ "format": "uri",
109109+ "description": "URL of a representative image"
110110+ },
111111+ "type": {
112112+ "type": "string",
113113+ "description": "Type of content (e.g., 'video', 'article')"
114114+ },
115115+ "retrievedAt": {
116116+ "type": "string",
117117+ "format": "datetime",
118118+ "description": "When the metadata was retrieved"
119119+ },
120120+ "doi": {
121121+ "type": "string",
122122+ "description": "Digital Object Identifier (DOI) for academic content"
123123+ },
124124+ "isbn": {
125125+ "type": "string",
126126+ "description": "International Standard Book Number (ISBN) for books"
127127+ }
128128+ }
129129+ }
130130+ }
131131+}
+51
lexicons/network/cosmik/collection.json
···11+{
22+ "lexicon": 1,
33+ "id": "network.cosmik.collection",
44+ "description": "A single record type for collections of cards.",
55+ "defs": {
66+ "main": {
77+ "type": "record",
88+ "description": "A record representing a collection of cards.",
99+ "key": "tid",
1010+ "record": {
1111+ "type": "object",
1212+ "required": ["name", "accessType"],
1313+ "properties": {
1414+ "name": {
1515+ "type": "string",
1616+ "description": "Name of the collection",
1717+ "maxLength": 100
1818+ },
1919+ "description": {
2020+ "type": "string",
2121+ "description": "Description of the collection",
2222+ "maxLength": 500
2323+ },
2424+ "accessType": {
2525+ "type": "string",
2626+ "description": "Access control for the collection",
2727+ "knownValues": ["OPEN", "CLOSED"]
2828+ },
2929+ "collaborators": {
3030+ "type": "array",
3131+ "description": "List of collaborator DIDs who can add cards to closed collections",
3232+ "items": {
3333+ "type": "string",
3434+ "description": "DID of a collaborator"
3535+ }
3636+ },
3737+ "createdAt": {
3838+ "type": "string",
3939+ "format": "datetime",
4040+ "description": "Timestamp when this collection was created (usually set by PDS)."
4141+ },
4242+ "updatedAt": {
4343+ "type": "string",
4444+ "format": "datetime",
4545+ "description": "Timestamp when this collection was last updated."
4646+ }
4747+ }
4848+ }
4949+ }
5050+ }
5151+}
+52
lexicons/network/cosmik/collectionLink.json
···11+{
22+ "lexicon": 1,
33+ "id": "network.cosmik.collectionLink",
44+ "description": "A record that links a card to a collection.",
55+ "defs": {
66+ "main": {
77+ "type": "record",
88+ "description": "A record representing the relationship between a card and a collection.",
99+ "key": "tid",
1010+ "record": {
1111+ "type": "object",
1212+ "required": ["collection", "card", "addedBy", "addedAt"],
1313+ "properties": {
1414+ "collection": {
1515+ "type": "ref",
1616+ "description": "Strong reference to the collection record.",
1717+ "ref": "com.atproto.repo.strongRef"
1818+ },
1919+ "card": {
2020+ "type": "ref",
2121+ "description": "Strong reference to the card record in the users library.",
2222+ "ref": "com.atproto.repo.strongRef"
2323+ },
2424+ "originalCard": {
2525+ "type": "ref",
2626+ "description": "Strong reference to the original card record (may be in another library).",
2727+ "ref": "com.atproto.repo.strongRef"
2828+ },
2929+ "addedBy": {
3030+ "type": "string",
3131+ "description": "DID of the user who added the card to the collection"
3232+ },
3333+ "addedAt": {
3434+ "type": "string",
3535+ "format": "datetime",
3636+ "description": "Timestamp when the card was added to the collection."
3737+ },
3838+ "createdAt": {
3939+ "type": "string",
4040+ "format": "datetime",
4141+ "description": "Timestamp when this link record was created (usually set by PDS)."
4242+ },
4343+ "provenance": {
4444+ "type": "ref",
4545+ "description": "Optional provenance information for this link.",
4646+ "ref": "network.cosmik.defs#provenance"
4747+ }
4848+ }
4949+ }
5050+ }
5151+ }
5252+}
+18
lexicons/network/cosmik/defs.json
···11+{
22+ "lexicon": 1,
33+ "id": "network.cosmik.defs",
44+ "description": "Common definitions for annotation types and references",
55+ "defs": {
66+ "provenance": {
77+ "type": "object",
88+ "description": "Represents the provenance or source of a record.",
99+ "properties": {
1010+ "via": {
1111+ "type": "ref",
1212+ "description": "Strong reference to the card that led to this record.",
1313+ "ref": "com.atproto.repo.strongRef"
1414+ }
1515+ }
1616+ }
1717+ }
1818+}
···11+export * as AtMarginAnnotation from "./types/at/margin/annotation.js";
22+export * as AtMarginBookmark from "./types/at/margin/bookmark.js";
33+export * as AtMarginCollection from "./types/at/margin/collection.js";
44+export * as AtMarginCollectionItem from "./types/at/margin/collectionItem.js";
55+export * as AtMarginHighlight from "./types/at/margin/highlight.js";
66+export * as AtMarginLike from "./types/at/margin/like.js";
77+export * as AtMarginProfile from "./types/at/margin/profile.js";
88+export * as AtMarginReply from "./types/at/margin/reply.js";
99+export * as ComAtprotoRepoStrongRef from "./types/com/atproto/repo/strongRef.js";
1010+export * as NetworkCosmikCard from "./types/network/cosmik/card.js";
1111+export * as NetworkCosmikCollection from "./types/network/cosmik/collection.js";
1212+export * as NetworkCosmikCollectionLink from "./types/network/cosmik/collectionLink.js";
1313+export * as NetworkCosmikDefs from "./types/network/cosmik/defs.js";
···11+import type {} from "@atcute/lexicons";
22+import * as v from "@atcute/lexicons/validations";
33+import type {} from "@atcute/lexicons/ambient";
44+import * as ComAtprotoRepoStrongRef from "../../com/atproto/repo/strongRef.js";
55+import * as NetworkCosmikDefs from "./defs.js";
66+77+const _mainSchema = /*#__PURE__*/ v.record(
88+ /*#__PURE__*/ v.tidString(),
99+ /*#__PURE__*/ v.object({
1010+ $type: /*#__PURE__*/ v.literal("network.cosmik.collectionLink"),
1111+ /**
1212+ * Timestamp when the card was added to the collection.
1313+ */
1414+ addedAt: /*#__PURE__*/ v.datetimeString(),
1515+ /**
1616+ * DID of the user who added the card to the collection
1717+ */
1818+ addedBy: /*#__PURE__*/ v.string(),
1919+ /**
2020+ * Strong reference to the card record in the users library.
2121+ */
2222+ get card() {
2323+ return ComAtprotoRepoStrongRef.mainSchema;
2424+ },
2525+ /**
2626+ * Strong reference to the collection record.
2727+ */
2828+ get collection() {
2929+ return ComAtprotoRepoStrongRef.mainSchema;
3030+ },
3131+ /**
3232+ * Timestamp when this link record was created (usually set by PDS).
3333+ */
3434+ createdAt: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.datetimeString()),
3535+ /**
3636+ * Strong reference to the original card record (may be in another library).
3737+ */
3838+ get originalCard() {
3939+ return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema);
4040+ },
4141+ /**
4242+ * Optional provenance information for this link.
4343+ */
4444+ get provenance() {
4545+ return /*#__PURE__*/ v.optional(NetworkCosmikDefs.provenanceSchema);
4646+ },
4747+ }),
4848+);
4949+5050+type main$schematype = typeof _mainSchema;
5151+5252+export interface mainSchema extends main$schematype {}
5353+5454+export const mainSchema = _mainSchema as mainSchema;
5555+5656+export interface Main extends v.InferInput<typeof mainSchema> {}
5757+5858+declare module "@atcute/lexicons/ambient" {
5959+ interface Records {
6060+ "network.cosmik.collectionLink": mainSchema;
6161+ }
6262+}
+23
src/lexicons/types/network/cosmik/defs.ts
···11+import type {} from "@atcute/lexicons";
22+import * as v from "@atcute/lexicons/validations";
33+import * as ComAtprotoRepoStrongRef from "../../com/atproto/repo/strongRef.js";
44+55+const _provenanceSchema = /*#__PURE__*/ v.object({
66+ $type: /*#__PURE__*/ v.optional(
77+ /*#__PURE__*/ v.literal("network.cosmik.defs#provenance"),
88+ ),
99+ /**
1010+ * Strong reference to the card that led to this record.
1111+ */
1212+ get via() {
1313+ return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema);
1414+ },
1515+});
1616+1717+type provenance$schematype = typeof _provenanceSchema;
1818+1919+export interface provenanceSchema extends provenance$schematype {}
2020+2121+export const provenanceSchema = _provenanceSchema as provenanceSchema;
2222+2323+export interface Provenance extends v.InferInput<typeof provenanceSchema> {}
+39
src/lib/atproto.ts
···11+import type { Client } from "@atcute/client";
22+import type { ActorIdentifier, Nsid } from "@atcute/lexicons";
33+44+export async function getRecord(client: Client, repo: string, collection: string, rkey: string) {
55+ return await client.get("com.atproto.repo.getRecord", {
66+ params: {
77+ repo: repo as ActorIdentifier,
88+ collection: collection as Nsid,
99+ rkey,
1010+ },
1111+ });
1212+}
1313+1414+export async function deleteRecord(client: Client, repo: string, collection: string, rkey: string) {
1515+ return await client.post("com.atproto.repo.deleteRecord", {
1616+ input: {
1717+ repo: repo as ActorIdentifier,
1818+ collection: collection as Nsid,
1919+ rkey,
2020+ },
2121+ });
2222+}
2323+2424+export async function putRecord<T = unknown>(client: Client, repo: string, collection: string, rkey: string, record: T) {
2525+ return await client.post("com.atproto.repo.putRecord", {
2626+ input: {
2727+ repo: repo as ActorIdentifier,
2828+ collection: collection as Nsid,
2929+ rkey,
3030+ record: record as unknown as { [key: string]: unknown },
3131+ },
3232+ });
3333+}
3434+3535+export async function getProfile(client: Client, actor: string) {
3636+ return await client.get("app.bsky.actor.getProfile", {
3737+ params: { actor: actor as ActorIdentifier },
3838+ });
3939+}
···11+import { readFileSync, writeFileSync } from "fs";
22+33+const targetVersion = process.env.npm_package_version;
44+55+// read minAppVersion from manifest.json and bump version to target version
66+const manifest = JSON.parse(readFileSync("manifest.json", "utf8"));
77+const { minAppVersion } = manifest;
88+manifest.version = targetVersion;
99+writeFileSync("manifest.json", JSON.stringify(manifest, null, "\t"));
1010+1111+// update versions.json with target version and minAppVersion from manifest.json
1212+// but only if the target version is not already in versions.json
1313+const versions = JSON.parse(readFileSync('versions.json', 'utf8'));
1414+if (!Object.values(versions).includes(minAppVersion)) {
1515+ versions[targetVersion] = minAppVersion;
1616+ writeFileSync('versions.json', JSON.stringify(versions, null, '\t'));
1717+}