diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 764bffe..e59ff45 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,9 +1,9 @@ name: Release Obsidian Plugin # Triggered by pushing a SemVer tag (v*). Builds the production bundle -# and attaches main.js, manifest.json, and logo SVGs to the GitHub -# Release - the same artefacts the Obsidian Community Plugins store -# expects to download from the GitHub release. +# and attaches only the files Obsidian downloads - main.js, manifest.json, +# and styles.css - to the GitHub Release. Obsidian ignores any other +# release assets, so none are attached. # # The Release is published under the BARE version tag (e.g. 1.1.0), not # the pushed v-prefixed tag. Obsidian matches manifest.json "version" @@ -17,6 +17,8 @@ on: permissions: contents: write + id-token: write + attestations: write jobs: build-and-release: @@ -65,9 +67,16 @@ jobs: - name: Collect release artefacts run: | mkdir -p release - cp main.js manifest.json logo-dark.svg logo-light.svg release/ + cp main.js manifest.json release/ if [ -f styles.css ]; then cp styles.css release/; fi + - name: Attest build provenance + uses: actions/attest-build-provenance@v2 + with: + subject-path: | + release/main.js + release/manifest.json + - name: Publish GitHub Release uses: softprops/action-gh-release@v2 with: @@ -78,8 +87,6 @@ jobs: files: | release/main.js release/manifest.json - release/logo-dark.svg - release/logo-light.svg release/styles.css fail_on_unmatched_files: false env: diff --git a/README.md b/README.md index 3b5a1d6..d5de635 100644 --- a/README.md +++ b/README.md @@ -51,8 +51,8 @@ Two-way OAuth sync between your Obsidian vault and your Unabyss memory. pnpm build ``` -3. Copy `main.js`, `manifest.json`, `logo-dark.svg`, and - `logo-light.svg` into `/.obsidian/plugins/unabyss/` +3. Copy `main.js`, `manifest.json`, and `styles.css` into + `/.obsidian/plugins/unabyss/` (create the folder if it doesn't exist). 4. Enable the plugin in Settings -> Community plugins. diff --git a/esbuild.config.mjs b/esbuild.config.mjs index ccbdc0d..4299329 100644 --- a/esbuild.config.mjs +++ b/esbuild.config.mjs @@ -1,6 +1,6 @@ import esbuild from "esbuild"; import process from "process"; -import builtins from "builtin-modules"; +import { builtinModules } from "module"; const banner = `/* THIS IS A GENERATED/BUNDLED FILE BY ESBUILD @@ -28,7 +28,8 @@ const context = await esbuild.context({ "@lezer/common", "@lezer/highlight", "@lezer/lr", - ...builtins, + ...builtinModules, + ...builtinModules.map((name) => `node:${name}`), ], format: "cjs", target: "es2018", diff --git a/manifest.json b/manifest.json index 914cc6d..6a610c0 100644 --- a/manifest.json +++ b/manifest.json @@ -1,9 +1,9 @@ { "id": "unabyss", "name": "Unabyss", - "version": "1.1.0", + "version": "1.1.1", "minAppVersion": "1.5.0", - "description": "Sync notes and exports between your Obsidian vault and Unabyss.", + "description": "Sync notes and exports between your vault and Unabyss.", "author": "Unabyss", "authorUrl": "https://unabyss.com", "isDesktopOnly": false diff --git a/package.json b/package.json index d83ac27..be74955 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,6 @@ "devDependencies": { "@types/jest": "^29.5.12", "@types/node": "^20.11.0", - "builtin-modules": "^3.3.0", "esbuild": "^0.21.5", "jest": "^29.7.0", "obsidian": "^1.5.7", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 74e4444..805fced 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,9 +14,6 @@ importers: '@types/node': specifier: ^20.11.0 version: 20.19.41 - builtin-modules: - specifier: ^3.3.0 - version: 3.3.0 esbuild: specifier: ^0.21.5 version: 0.21.5 @@ -575,10 +572,6 @@ packages: buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - builtin-modules@3.3.0: - resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} - engines: {node: '>=6'} - callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -2020,8 +2013,6 @@ snapshots: buffer-from@1.1.2: {} - builtin-modules@3.3.0: {} - callsites@3.1.0: {} camelcase@5.3.1: {} diff --git a/src/apiClient.ts b/src/apiClient.ts index 37a9281..1e21f7c 100644 --- a/src/apiClient.ts +++ b/src/apiClient.ts @@ -151,7 +151,7 @@ export class UnabyssApiClient { } try { await this.refreshTokens(); - } catch (err) { + } catch { await this.clearAuth("refresh_failed"); throw new AuthExpiredError(); } diff --git a/src/main.ts b/src/main.ts index d7fdefb..93b3b9a 100644 --- a/src/main.ts +++ b/src/main.ts @@ -232,14 +232,14 @@ export default class UnabyssPlugin extends Plugin { this.settings.outboundEnabled ? this.runOutboundSync(true) .then((report) => ({ ok: true as const, report })) - .catch((err) => ({ ok: false as const, err })) + .catch((err: unknown) => ({ ok: false as const, err })) : Promise.resolve(null); const inboundP: Promise | null> = this.settings.inboundEnabled ? this.runInboundSync(true) .then((report) => ({ ok: true as const, report })) - .catch((err) => ({ ok: false as const, err })) + .catch((err: unknown) => ({ ok: false as const, err })) : Promise.resolve(null); const [outboundSettled, inboundSettled] = await Promise.allSettled([outboundP, inboundP]); diff --git a/src/settings.ts b/src/settings.ts index 184800a..4219e61 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -61,29 +61,42 @@ export class UnabyssSettingTab extends PluginSettingTab { private renderHeader(containerEl: HTMLElement): void { const header = containerEl.createDiv({ cls: "unabyss-settings-header" }); - header.style.display = "flex"; - header.style.alignItems = "center"; - header.style.gap = "10px"; - header.style.marginBottom = "18px"; - - const logo = header.createEl("img", { cls: "unabyss-settings-logo" }); - logo.alt = "Unabyss"; - logo.src = this.pluginLogoResourcePath(); - logo.style.width = "32px"; - logo.style.height = "32px"; - logo.style.flexShrink = "0"; - - const title = header.createEl("h2", { text: "Unabyss" }); - title.style.margin = "0"; + this.renderLogo(header); + header.createDiv({ cls: "unabyss-settings-title", text: "Unabyss" }); } - private pluginLogoResourcePath(): string { - const logoFile = document.body.classList.contains("theme-dark") - ? "logo-dark.svg" - : "logo-light.svg"; - return this.app.vault.adapter.getResourcePath( - `.obsidian/plugins/${this.plugin.manifest.id}/${logoFile}`, - ); + /** + * Renders the Unabyss mark as inline SVG that inherits the current + * theme text colour, so the plugin ships no extra image assets and + * adapts to light/dark automatically. + */ + private renderLogo(parent: HTMLElement): void { + const dotOpacities = [ + 0.28, 0.26, 0.89, 0.65, + 0.56, 0.7, 0.88, 0.69, + 0.57, 0.26, 0.62, 0.5, + 0.74, 0.08, 0.13, 0.98, + ]; + const coords = [4, 12, 20, 28]; + const svg = parent.createSvg("svg", { + cls: "unabyss-settings-logo", + attr: { viewBox: "0 0 32 32", width: 32, height: 32 }, + }); + let index = 0; + for (const cy of coords) { + for (const cx of coords) { + svg.createSvg("circle", { + attr: { + cx, + cy, + r: 3, + fill: "currentColor", + "fill-opacity": dotOpacities[index], + }, + }); + index++; + } + } } private renderApiBaseUrl(containerEl: HTMLElement): void { @@ -111,11 +124,6 @@ export class UnabyssSettingTab extends PluginSettingTab { } const auth = this.plugin.settings.auth; const banner = containerEl.createDiv({ cls: "unabyss-connection-banner" }); - banner.style.marginBottom = "16px"; - banner.style.padding = "12px 14px"; - banner.style.borderRadius = "8px"; - banner.style.border = "1px solid var(--background-modifier-border)"; - banner.style.background = "var(--background-secondary)"; banner.createEl("p", { text: @@ -128,11 +136,7 @@ export class UnabyssSettingTab extends PluginSettingTab { cls: "setting-item-description", }); - const actions = banner.createDiv(); - actions.style.display = "flex"; - actions.style.flexWrap = "wrap"; - actions.style.gap = "8px"; - actions.style.marginTop = "10px"; + const actions = banner.createDiv({ cls: "unabyss-connection-banner-actions" }); const syncBtn = actions.createEl("button", { text: "Sync now" }); syncBtn.classList.add("mod-cta"); @@ -221,7 +225,7 @@ export class UnabyssSettingTab extends PluginSettingTab { } private renderOutboundSection(containerEl: HTMLElement): void { - containerEl.createEl("h3", { text: "Obsidian \u2192 Unabyss (outbound)" }); + new Setting(containerEl).setName("Obsidian \u2192 Unabyss (outbound)").setHeading(); new Setting(containerEl) .setName("Sync outbound") @@ -267,7 +271,7 @@ export class UnabyssSettingTab extends PluginSettingTab { } private renderInboundSection(containerEl: HTMLElement): void { - containerEl.createEl("h3", { text: "Unabyss \u2192 Obsidian (inbound)" }); + new Setting(containerEl).setName("Unabyss \u2192 Obsidian (inbound)").setHeading(); new Setting(containerEl) .setName("Sync inbound") @@ -322,7 +326,7 @@ export class UnabyssSettingTab extends PluginSettingTab { } private renderAdvancedSection(containerEl: HTMLElement): void { - containerEl.createEl("h3", { text: "Advanced" }); + new Setting(containerEl).setName("Advanced").setHeading(); this.renderApiBaseUrl(containerEl); new Setting(containerEl) .setName("Force full resync") @@ -359,26 +363,16 @@ export class UnabyssSettingTab extends PluginSettingTab { return; } const chipsRow = containerEl.createDiv({ cls: "unabyss-folder-chip-row" }); - chipsRow.style.display = "flex"; - chipsRow.style.flexWrap = "wrap"; - chipsRow.style.gap = "8px"; - chipsRow.style.marginBottom = "12px"; for (const folder of folders) { const chip = chipsRow.createDiv({ cls: "unabyss-folder-chip" }); - chip.style.padding = "4px 8px"; - chip.style.border = "1px solid var(--background-modifier-border)"; - chip.style.borderRadius = "4px"; - chip.style.display = "flex"; - chip.style.alignItems = "center"; - chip.style.gap = "6px"; chip.createSpan({ text: folder }); - const remove = chip.createEl("button", { text: "x" }); - remove.style.background = "transparent"; - remove.style.border = "none"; - remove.style.cursor = "pointer"; - remove.addEventListener("click", async () => { + const remove = chip.createEl("button", { + text: "x", + cls: "unabyss-folder-chip-remove", + }); + remove.addEventListener("click", () => { const next = folders.filter((entry) => entry !== folder); - await save(next); + void save(next); }); } } diff --git a/src/syncInbound.ts b/src/syncInbound.ts index d3d4c3d..55e385d 100644 --- a/src/syncInbound.ts +++ b/src/syncInbound.ts @@ -205,7 +205,7 @@ async function applyDeletion( }; } if (deps.deleteBehaviour === "delete") { - await deps.app.vault.trash(existing, true); + await deps.app.fileManager.trashFile(existing); return { exportId: row.id, title: row.title, diff --git a/styles.css b/styles.css new file mode 100644 index 0000000..80f9995 --- /dev/null +++ b/styles.css @@ -0,0 +1,56 @@ +.unabyss-settings-header { + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 18px; +} + +.unabyss-settings-logo { + width: 32px; + height: 32px; + flex-shrink: 0; + color: var(--text-normal); +} + +.unabyss-settings-title { + margin: 0; + font-size: var(--font-ui-large); + font-weight: var(--font-bold); +} + +.unabyss-connection-banner { + margin-bottom: 16px; + padding: 12px 14px; + border-radius: 8px; + border: 1px solid var(--background-modifier-border); + background: var(--background-secondary); +} + +.unabyss-connection-banner-actions { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-top: 10px; +} + +.unabyss-folder-chip-row { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-bottom: 12px; +} + +.unabyss-folder-chip { + padding: 4px 8px; + border: 1px solid var(--background-modifier-border); + border-radius: 4px; + display: flex; + align-items: center; + gap: 6px; +} + +.unabyss-folder-chip-remove { + background: transparent; + border: none; + cursor: pointer; +} diff --git a/versions.json b/versions.json index edad0bf..84a655d 100644 --- a/versions.json +++ b/versions.json @@ -1,4 +1,7 @@ { "0.1.0": "1.5.0", - "1.0.0": "1.5.0" + "1.0.0": "1.5.0", + "1.0.1": "1.5.0", + "1.1.0": "1.5.0", + "1.1.1": "1.5.0" }