Skip to content

Commit 3a2d2d3

Browse files
leonardsellemclaude
andcommitted
Initial release — Plaud Sync for Obsidian v1.0.0
Sync Plaud voice recordings into Obsidian Markdown notes with incremental sync, retry/backoff, secure token storage, and full transcript/summary/highlights rendering. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
0 parents  commit 3a2d2d3

43 files changed

Lines changed: 8948 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.editorconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# top-most EditorConfig file
2+
root = true
3+
4+
[*]
5+
charset = utf-8
6+
end_of_line = lf
7+
insert_final_newline = true
8+
indent_style = tab
9+
indent_size = 4
10+
tab_width = 4

.github/workflows/lint.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: Node.js build
2+
3+
on:
4+
push:
5+
branches: ["**"]
6+
pull_request:
7+
branches: ["**"]
8+
9+
jobs:
10+
build:
11+
runs-on: ubuntu-latest
12+
13+
strategy:
14+
matrix:
15+
node-version: [20.x, 22.x]
16+
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
17+
18+
steps:
19+
- uses: actions/checkout@v4
20+
- name: Use Node.js ${{ matrix.node-version }}
21+
uses: actions/setup-node@v4
22+
with:
23+
node-version: ${{ matrix.node-version }}
24+
cache: "npm"
25+
- run: npm ci
26+
- run: npm run build --if-present
27+
- run: npm run lint
28+

.gitignore

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# vscode
2+
.vscode
3+
4+
# Intellij
5+
*.iml
6+
.idea
7+
8+
# npm
9+
node_modules
10+
11+
# Don't include the compiled main.js file in the repo.
12+
# They should be uploaded to GitHub releases instead.
13+
main.js
14+
15+
# Exclude sourcemaps
16+
*.map
17+
18+
# obsidian
19+
data.json
20+
21+
# Exclude macOS Finder (System Explorer) View States
22+
.DS_Store

.npmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
tag-version-prefix=""

AGENTS.md

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
# Obsidian community plugin
2+
3+
## Project overview
4+
5+
- Target: Obsidian Community Plugin (TypeScript → bundled JavaScript).
6+
- Entry point: `main.ts` compiled to `main.js` and loaded by Obsidian.
7+
- Required release artifacts: `main.js`, `manifest.json`, and optional `styles.css`.
8+
9+
## Environment & tooling
10+
11+
- Node.js: use current LTS (Node 18+ recommended).
12+
- **Package manager: npm** (required for this sample - `package.json` defines npm scripts and dependencies).
13+
- **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`.
14+
- Types: `obsidian` type definitions.
15+
16+
**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.
17+
18+
### Install
19+
20+
```bash
21+
npm install
22+
```
23+
24+
### Dev (watch)
25+
26+
```bash
27+
npm run dev
28+
```
29+
30+
### Production build
31+
32+
```bash
33+
npm run build
34+
```
35+
36+
## Linting
37+
38+
- To use eslint install eslint from terminal: `npm install -g eslint`
39+
- To use eslint to analyze this project use this command: `eslint main.ts`
40+
- eslint will then create a report with suggestions for code improvement by file and line number.
41+
- 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/`
42+
43+
## File & folder conventions
44+
45+
- **Organize code into multiple files**: Split functionality across separate modules rather than putting everything in `main.ts`.
46+
- Source lives in `src/`. Keep `main.ts` small and focused on plugin lifecycle (loading, unloading, registering commands).
47+
- **Example file structure**:
48+
```
49+
src/
50+
main.ts # Plugin entry point, lifecycle management
51+
settings.ts # Settings interface and defaults
52+
commands/ # Command implementations
53+
command1.ts
54+
command2.ts
55+
ui/ # UI components, modals, views
56+
modal.ts
57+
view.ts
58+
utils/ # Utility functions, helpers
59+
helpers.ts
60+
constants.ts
61+
types.ts # TypeScript interfaces and types
62+
```
63+
- **Do not commit build artifacts**: Never commit `node_modules/`, `main.js`, or other generated files to version control.
64+
- Keep the plugin small. Avoid large dependencies. Prefer browser-compatible packages.
65+
- 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`).
66+
67+
## Manifest rules (`manifest.json`)
68+
69+
- Must include (non-exhaustive):
70+
- `id` (plugin ID; for local dev it should match the folder name)
71+
- `name`
72+
- `version` (Semantic Versioning `x.y.z`)
73+
- `minAppVersion`
74+
- `description`
75+
- `isDesktopOnly` (boolean)
76+
- Optional: `author`, `authorUrl`, `fundingUrl` (string or map)
77+
- Never change `id` after release. Treat it as stable API.
78+
- Keep `minAppVersion` accurate when using newer APIs.
79+
- Canonical requirements are coded here: https://github.com/obsidianmd/obsidian-releases/blob/master/.github/workflows/validate-plugin-entry.yml
80+
81+
## Testing
82+
83+
- Manual install for testing: copy `main.js`, `manifest.json`, `styles.css` (if any) to:
84+
```
85+
<Vault>/.obsidian/plugins/<plugin-id>/
86+
```
87+
- Reload Obsidian and enable the plugin in **Settings → Community plugins**.
88+
89+
## Commands & settings
90+
91+
- Any user-facing commands should be added via `this.addCommand(...)`.
92+
- If the plugin has configuration, provide a settings tab and sensible defaults.
93+
- Persist settings using `this.loadData()` / `this.saveData()`.
94+
- Use stable command IDs; avoid renaming once released.
95+
96+
## Versioning & releases
97+
98+
- Bump `version` in `manifest.json` (SemVer) and update `versions.json` to map plugin version → minimum app version.
99+
- Create a GitHub release whose tag exactly matches `manifest.json`'s `version`. Do not use a leading `v`.
100+
- Attach `manifest.json`, `main.js`, and `styles.css` (if present) to the release as individual assets.
101+
- After the initial release, follow the process to add/update your plugin in the community catalog as required.
102+
103+
## Security, privacy, and compliance
104+
105+
Follow Obsidian's **Developer Policies** and **Plugin Guidelines**. In particular:
106+
107+
- Default to local/offline operation. Only make network requests when essential to the feature.
108+
- 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.
109+
- Never execute remote code, fetch and eval scripts, or auto-update plugin code outside of normal releases.
110+
- Minimize scope: read/write only what's necessary inside the vault. Do not access files outside the vault.
111+
- Clearly disclose any external services used, data sent, and risks.
112+
- Respect user privacy. Do not collect vault contents, filenames, or personal information unless absolutely necessary and explicitly consented.
113+
- Avoid deceptive patterns, ads, or spammy notifications.
114+
- Register and clean up all DOM, app, and interval listeners using the provided `register*` helpers so the plugin unloads safely.
115+
116+
## UX & copy guidelines (for UI text, commands, settings)
117+
118+
- Prefer sentence case for headings, buttons, and titles.
119+
- Use clear, action-oriented imperatives in step-by-step copy.
120+
- Use **bold** to indicate literal UI labels. Prefer "select" for interactions.
121+
- Use arrow notation for navigation: **Settings → Community plugins**.
122+
- Keep in-app strings short, consistent, and free of jargon.
123+
124+
## Performance
125+
126+
- Keep startup light. Defer heavy work until needed.
127+
- Avoid long-running tasks during `onload`; use lazy initialization.
128+
- Batch disk access and avoid excessive vault scans.
129+
- Debounce/throttle expensive operations in response to file system events.
130+
131+
## Coding conventions
132+
133+
- TypeScript with `"strict": true` preferred.
134+
- **Keep `main.ts` minimal**: Focus only on plugin lifecycle (onload, onunload, addCommand calls). Delegate all feature logic to separate modules.
135+
- **Split large files**: If any file exceeds ~200-300 lines, consider breaking it into smaller, focused modules.
136+
- **Use clear module boundaries**: Each file should have a single, well-defined responsibility.
137+
- Bundle everything into `main.js` (no unbundled runtime deps).
138+
- Avoid Node/Electron APIs if you want mobile compatibility; set `isDesktopOnly` accordingly.
139+
- Prefer `async/await` over promise chains; handle errors gracefully.
140+
141+
## Mobile
142+
143+
- Where feasible, test on iOS and Android.
144+
- Don't assume desktop-only behavior unless `isDesktopOnly` is `true`.
145+
- Avoid large in-memory structures; be mindful of memory and storage constraints.
146+
147+
## Agent do/don't
148+
149+
**Do**
150+
- Add commands with stable IDs (don't rename once released).
151+
- Provide defaults and validation in settings.
152+
- Write idempotent code paths so reload/unload doesn't leak listeners or intervals.
153+
- Use `this.register*` helpers for everything that needs cleanup.
154+
155+
**Don't**
156+
- Introduce network calls without an obvious user-facing reason and documentation.
157+
- Ship features that require cloud services without clear disclosure and explicit opt-in.
158+
- Store or transmit vault contents unless essential and consented.
159+
160+
## Common tasks
161+
162+
### Organize code across multiple files
163+
164+
**main.ts** (minimal, lifecycle only):
165+
```ts
166+
import { Plugin } from "obsidian";
167+
import { MySettings, DEFAULT_SETTINGS } from "./settings";
168+
import { registerCommands } from "./commands";
169+
170+
export default class MyPlugin extends Plugin {
171+
settings: MySettings;
172+
173+
async onload() {
174+
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
175+
registerCommands(this);
176+
}
177+
}
178+
```
179+
180+
**settings.ts**:
181+
```ts
182+
export interface MySettings {
183+
enabled: boolean;
184+
apiKey: string;
185+
}
186+
187+
export const DEFAULT_SETTINGS: MySettings = {
188+
enabled: true,
189+
apiKey: "",
190+
};
191+
```
192+
193+
**commands/index.ts**:
194+
```ts
195+
import { Plugin } from "obsidian";
196+
import { doSomething } from "./my-command";
197+
198+
export function registerCommands(plugin: Plugin) {
199+
plugin.addCommand({
200+
id: "do-something",
201+
name: "Do something",
202+
callback: () => doSomething(plugin),
203+
});
204+
}
205+
```
206+
207+
### Add a command
208+
209+
```ts
210+
this.addCommand({
211+
id: "your-command-id",
212+
name: "Do the thing",
213+
callback: () => this.doTheThing(),
214+
});
215+
```
216+
217+
### Persist settings
218+
219+
```ts
220+
interface MySettings { enabled: boolean }
221+
const DEFAULT_SETTINGS: MySettings = { enabled: true };
222+
223+
async onload() {
224+
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
225+
await this.saveData(this.settings);
226+
}
227+
```
228+
229+
### Register listeners safely
230+
231+
```ts
232+
this.registerEvent(this.app.workspace.on("file-open", f => { /* ... */ }));
233+
this.registerDomEvent(window, "resize", () => { /* ... */ });
234+
this.registerInterval(window.setInterval(() => { /* ... */ }, 1000));
235+
```
236+
237+
## Troubleshooting
238+
239+
- 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>/`.
240+
- Build issues: if `main.js` is missing, run `npm run build` or `npm run dev` to compile your TypeScript source code.
241+
- Commands not appearing: verify `addCommand` runs after `onload` and IDs are unique.
242+
- Settings not persisting: ensure `loadData`/`saveData` are awaited and you re-render the UI after changes.
243+
- Mobile-only issues: confirm you're not using desktop-only APIs; check `isDesktopOnly` and adjust.
244+
245+
## References
246+
247+
- Obsidian sample plugin: https://github.com/obsidianmd/obsidian-sample-plugin
248+
- API documentation: https://docs.obsidian.md
249+
- Developer policies: https://docs.obsidian.md/Developer+policies
250+
- Plugin guidelines: https://docs.obsidian.md/Plugins/Releasing/Plugin+guidelines
251+
- Style guide: https://help.obsidian.md/style-guide

CONTRIBUTING.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Contributing to Plaud Sync for Obsidian
2+
3+
Thanks for your interest in contributing! This plugin uses a reverse-engineered API, so community help with keeping it working is especially valuable.
4+
5+
## Getting started
6+
7+
1. Fork and clone the repo
8+
2. Install dependencies: `npm ci`
9+
3. Start dev mode: `npm run dev`
10+
4. Symlink or copy the plugin into a test vault's `.obsidian/plugins/plaud-sync/` folder
11+
5. Enable the plugin in Obsidian and reload after changes
12+
13+
## Development workflow
14+
15+
```bash
16+
npm run dev # watch mode — rebuilds on save
17+
npm run build # typecheck + production build
18+
npm run test # run test suite
19+
npm run lint # eslint
20+
```
21+
22+
All four commands must pass before submitting a PR.
23+
24+
## Submitting changes
25+
26+
1. Create a branch from `main`
27+
2. Make your changes with clear, focused commits
28+
3. Add or update tests for any new or changed behavior
29+
4. Run `npm run test && npm run lint && npm run build` to verify
30+
5. Open a pull request against `main`
31+
32+
## Reporting bugs
33+
34+
Open an issue with:
35+
36+
- Obsidian version and OS
37+
- Steps to reproduce
38+
- Expected vs. actual behavior
39+
- Any relevant error messages from the developer console (`Ctrl/Cmd+Shift+I`)
40+
41+
## Reporting API changes
42+
43+
Since this plugin depends on a reverse-engineered API, Plaud may change their endpoints at any time. If sync stops working after a Plaud update:
44+
45+
1. Open the Plaud web app and check the Network tab in DevTools
46+
2. Note any changed endpoints, response formats, or headers
47+
3. Open an issue with your findings
48+
49+
## Code style
50+
51+
- TypeScript with `strict: true`
52+
- Follow existing patterns in the codebase
53+
- Keep modules focused — one responsibility per file
54+
- Prefer `async/await` over promise chains
55+
56+
## License
57+
58+
By contributing, you agree that your contributions will be licensed under the [MIT License](LICENSE).

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 Leonard Sellem
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

0 commit comments

Comments
 (0)