Skip to content

Commit 2d7a65a

Browse files
authored
YAML note properties support (#100)
- Added secure token storage using Obsidian's [SecretStorage API](https://docs.obsidian.md/Reference/TypeScript+API/SecretStorage). - Authentication tokens are now stored securely using the platform's native secret storage (Keychain on macOS, Credential Manager on Windows, libsecret on Linux). - Tokens are no longer stored in `data.json`, preventing accidental exposure when syncing vaults. - Existing tokens are automatically migrated to secure storage on plugin load. - New password-style input with visibility toggle in Git settings. - Requires Obsidian 1.11.4 or later. - Added configurable frontmatter output format setting. - YAML format is now the default (more readable and standard for static site generators). - JSON format available as an option for backwards compatibility. - Configurable in Settings > Note properties (frontmatter). - Improved diff view styling.
1 parent f31a68d commit 2d7a65a

File tree

18 files changed

+482
-55
lines changed

18 files changed

+482
-55
lines changed

docs/Changelog.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
title: Roadmap and Changelog
33
description: Changelog and feature roadmap for Quartz Syncer.
44
created: 2025-05-16T12:59:31Z+0200
5-
modified: 2026-01-08T14:46:23Z+0100
5+
modified: 2026-01-09T09:51:37Z+0100
66
publish: true
77
---
88

@@ -24,6 +24,20 @@ publish: true
2424

2525
## Released
2626

27+
### Version 1.9.1
28+
29+
- Added secure token storage using Obsidian's [SecretStorage API](https://docs.obsidian.md/Reference/TypeScript+API/SecretStorage).
30+
- Authentication tokens are now stored securely using the platform's native secret storage (Keychain on macOS, Credential Manager on Windows, libsecret on Linux).
31+
- Tokens are no longer stored in `data.json`, preventing accidental exposure when syncing vaults.
32+
- Existing tokens are automatically migrated to secure storage on plugin load.
33+
- New password-style input with visibility toggle in Git settings.
34+
- Requires Obsidian 1.11.4 or later.
35+
- Added configurable frontmatter output format setting.
36+
- YAML format is now the default (more readable and standard for static site generators).
37+
- JSON format available as an option for backwards compatibility.
38+
- Configurable in Settings > Note properties (frontmatter).
39+
- Improved diff view styling.
40+
2741
### Version 1.9.0
2842

2943
- Replaced Octokit-based implementation with Isomorphic Git-based implementation.

docs/Settings/Git/Access Token.md

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,40 @@
22
title: Access Token
33
description: Personal access token or password for authentication.
44
created: 2026-01-08T14:00:00Z+0100
5-
modified: 2026-01-08T14:00:00Z+0100
5+
modified: 2026-01-09T10:00:54Z+0100
66
publish: true
77
tags: [settings/git]
8-
default_value: '`""`'
98
---
109

11-
The personal access token or password used for authentication.
10+
> [!WARNING] Requirements
11+
> Secure token storage requires **Obsidian 1.11.4 or later**.
1212
13-
Refer to the [authentication guides](../Guides/index.md) for more information on how to generate tokens for different providers.
13+
The personal access token or password used for authentication with your Git provider.
14+
15+
## Secure Storage
16+
17+
As of version 1.9.1, tokens are stored securely using Obsidian's SecretStorage API. This uses your operating system's native secret storage:
18+
19+
- **macOS**: Keychain
20+
- **Windows**: Credential Manager
21+
- **Linux**: libsecret (GNOME Keyring, KWallet, etc.)
22+
23+
This means your token is never written to `data.json` or any other plain text file, preventing accidental exposure when syncing your vault between devices.
24+
25+
## Token Input
26+
27+
The token input field includes:
28+
29+
- **Status indicator**: Shows whether a token is currently stored ("Token stored securely") or not ("No token set").
30+
- **Password field**: Enter your token in a masked input field for privacy.
31+
- **Visibility toggle**: Click the eye icon to show/hide the token while typing.
32+
- **Save/Update button**: Store the token in secure storage.
33+
- **Clear button**: Remove the stored token (only shown when a token exists).
34+
35+
## Migration
36+
37+
If you're upgrading from a version prior to 1.9.1, your existing token will be automatically migrated to secure storage and removed from `data.json` on first load.
38+
39+
## Generating Tokens
40+
41+
Refer to the [[Setup Guide#Choose Your Git Provider|setup guide for your Git provider]] for instructions on generating tokens for different Git providers.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
---
2+
title: Frontmatter format
3+
description: Output format for frontmatter/note properties in published notes.
4+
created: 2026-01-09T07:32:00Z+0100
5+
modified: 2026-01-09T07:32:00Z+0100
6+
publish: true
7+
tags: [settings/frontmatter]
8+
default_value: "`yaml`"
9+
---
10+
11+
This setting controls the output format for frontmatter in your published notes.
12+
13+
## Options
14+
15+
- **YAML** (default): Human-readable format. Obsidian default, and standard for most static site generators including Quartz.
16+
- **JSON**: Legacy format, outputs frontmatter as a single JSON object.
17+
18+
## Example output
19+
20+
### YAML format
21+
22+
```yaml
23+
---
24+
publish: true
25+
title: My Note
26+
tags:
27+
- tag1
28+
- tag2
29+
created: Jan 09, 2026 7:32 AM
30+
---
31+
```
32+
33+
### JSON format
34+
35+
```json
36+
---
37+
{"publish":true,"title":"My Note","tags":["tag1","tag2"],"created":"Jan 09, 2026 7:32 AM"}
38+
---
39+
```

main.ts

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import ObsidianFrontMatterEngine from "src/publishFile/ObsidianFrontMatterEngine
88
import QuartzSyncerSiteManager from "src/repositoryConnection/QuartzSyncerSiteManager";
99
import { QuartzSyncerSettingTab } from "./src/views/QuartzSyncerSettingTab";
1010
import { DataStore } from "src/publishFile/DataStore";
11+
import { SecretStorageService } from "src/utils/SecretStorageService";
1112
import Logger from "js-logger";
1213

1314
/**
@@ -23,7 +24,6 @@ const DEFAULT_SETTINGS: QuartzSyncerSettings = {
2324
auth: {
2425
type: "basic",
2526
username: "",
26-
secret: "",
2727
},
2828
providerHint: "github",
2929
},
@@ -48,6 +48,7 @@ const DEFAULT_SETTINGS: QuartzSyncerSettings = {
4848
usePermalink: false,
4949

5050
includeAllFrontmatter: false,
51+
frontmatterFormat: "yaml",
5152

5253
/**
5354
* @privateRemarks
@@ -143,6 +144,7 @@ export default class QuartzSyncer extends Plugin {
143144
settings!: QuartzSyncerSettings;
144145
appVersion!: string;
145146
datastore!: DataStore;
147+
secretStorageService!: SecretStorageService;
146148

147149
publishModal!: PublicationCenter;
148150

@@ -214,6 +216,12 @@ export default class QuartzSyncer extends Plugin {
214216

215217
this.migrateGitHubSettings();
216218

219+
this.secretStorageService = new SecretStorageService(this.app);
220+
221+
await this.secretStorageService.migrateFromSettings(this.settings, () =>
222+
this.saveSettings(),
223+
);
224+
217225
if (!this.datastore && this.settings.useCache) {
218226
this.datastore = new DataStore(
219227
this.app.vault.getName(),
@@ -222,8 +230,6 @@ export default class QuartzSyncer extends Plugin {
222230
);
223231
}
224232

225-
// Check if the plugin has been updated
226-
// If so, clear the cache
227233
if (!this.settings || this.settings.pluginVersion !== this.appVersion) {
228234
await this.clearCacheForAllFiles(true);
229235
this.settings.pluginVersion = this.appVersion;
@@ -277,14 +283,20 @@ export default class QuartzSyncer extends Plugin {
277283
}
278284
}
279285

280-
/**
281-
* Saves the plugin settings to data.json.
282-
* This method is called after any changes to the settings.
283-
*/
284286
async saveSettings(): Promise<void> {
285287
await this.saveData(this.settings);
286288
}
287289

290+
getGitSettingsWithSecret(): typeof this.settings.git {
291+
return {
292+
...this.settings.git,
293+
auth: {
294+
...this.settings.git.auth,
295+
secret: this.secretStorageService.getToken() || undefined,
296+
},
297+
};
298+
}
299+
288300
/**
289301
* Adds commands to the plugin.
290302
* These commands can be triggered from the command palette or ribbon icon.
@@ -541,6 +553,7 @@ export default class QuartzSyncer extends Plugin {
541553
const siteManager = new QuartzSyncerSiteManager(
542554
this.app.metadataCache,
543555
this.settings,
556+
this.getGitSettingsWithSecret(),
544557
);
545558

546559
const publisher = new Publisher(

manifest.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"id": "quartz-syncer",
33
"name": "Quartz Syncer",
4-
"version": "1.9.0",
5-
"minAppVersion": "1.8.10",
4+
"version": "1.9.1",
5+
"minAppVersion": "1.11.4",
66
"description": "Manage and publish your notes to Quartz, the fast, batteries-included static-site generator.",
77
"author": "Emile Bangma",
88
"authorUrl": "https://github.com/saberzero1",

package-lock.json

Lines changed: 9 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "quartz-syncer",
3-
"version": "1.9.0",
3+
"version": "1.9.1",
44
"description": "A plugin used for publishing notes to Quartz",
55
"main": "main.js",
66
"scripts": {
@@ -42,7 +42,7 @@
4242
"husky": "^8.0.3",
4343
"jest": "^29.7.0",
4444
"lint-staged": "^16.1.0",
45-
"obsidian": "^1.10.0",
45+
"obsidian": "^1.11.4",
4646
"prettier": "^3.0.3",
4747
"prettier-plugin-svelte": "^3.0.3",
4848
"svelte": "^4.2.0",

scripts/generateSyncerSettings.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ const quartzSettings = {
3333
cache: "{}",
3434

3535
includeAllFrontmatter: false,
36+
frontmatterFormat: "yaml",
3637

3738
applyEmbeds: true,
3839

src/compiler/FrontmatterCompiler.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { FrontMatterCache } from "obsidian";
1+
import { FrontMatterCache, stringifyYaml } from "obsidian";
22
import { sanitizePermalink } from "src/utils/utils";
33
import QuartzSyncerSettings from "src/models/settings";
44
import { PublishFile } from "src/publishFile/PublishFile";
@@ -94,9 +94,12 @@ export class FrontmatterCompiler {
9494
? { ...publishedFrontMatter, ...fileFrontMatter }
9595
: publishedFrontMatter;
9696

97-
const frontMatterString = JSON.stringify(fullFrontMatter);
97+
const frontMatterString =
98+
this.settings.frontmatterFormat === "json"
99+
? JSON.stringify(fullFrontMatter) + "\n"
100+
: stringifyYaml(fullFrontMatter);
98101

99-
return `---\n${frontMatterString}\n---\n`;
102+
return `---\n${frontMatterString}---\n`;
100103
}
101104

102105
private addPermalink =

src/models/settings.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ export type GitProviderHint =
2727

2828
export type DiffViewStyle = "split" | "unified" | "auto";
2929

30+
export type FrontmatterFormat = "yaml" | "json";
31+
3032
/**
3133
* Generic Git remote settings.
3234
* Works with any Git provider (GitHub, GitLab, Bitbucket, self-hosted, etc.)
@@ -83,6 +85,13 @@ export default interface QuartzSyncerSettings {
8385

8486
includeAllFrontmatter: boolean;
8587

88+
/**
89+
* Output format for frontmatter in published notes.
90+
* - "yaml": Output frontmatter as YAML (default, more readable)
91+
* - "json": Output frontmatter as JSON (legacy behavior)
92+
*/
93+
frontmatterFormat: FrontmatterFormat;
94+
8695
/**
8796
* @privateRemarks
8897
*

0 commit comments

Comments
 (0)