Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# GitHub configuration
GITHUB_REPO=
GITHUB_TOKEN=
GITHUB_USERNAME=

# Garden settings
GARDEN_BASE_URL=

# Forestry.md settings
FORESTRY_BASE_URL=https://api.forestry.md/app
FORESTRY_PAGE_NAME=
FORESTRY_API_KEY=
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ GITHUB_REPO=
GITHUB_TOKEN=
GITHUB_USERNAME=

# Forestry.md settings (if you're using Forestry.md)
FORESTRY_BASE_URL=https://api.forestry.md/app
FORESTRY_PAGE_NAME=
FORESTRY_API_KEY=
```

Note: this repository uses prettier and eslint to enforce code formatting and style. It is recommended to install these to your IDE for automatic formatting and error highlighting.
Expand Down
9 changes: 8 additions & 1 deletion esbuild.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import process from "process";
import builtins from 'builtin-modules'
import esbuildSvelte from "esbuild-svelte";
import sveltePreprocess from "svelte-preprocess";
import dotenv from 'dotenv';

// Load environment variables from .env file
dotenv.config();

const banner =
`/*
Expand Down Expand Up @@ -31,5 +35,8 @@ esbuild.build({
compilerOptions: { css: 'injected'},
preprocess: sveltePreprocess(),
}),
],
],
define: {
'process.env.FORESTRY_BASE_URL': JSON.stringify(process.env.FORESTRY_BASE_URL || "https://api.forestry.md/app"),
},
}).catch(() => process.exit(1));
265 changes: 225 additions & 40 deletions main.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,28 @@
import { Notice, Platform, Plugin, Workspace, addIcon } from "obsidian";
import {
Notice,
Platform,
Plugin,
Workspace,
addIcon,
TFile,
Modal,
App,
} from "obsidian";
import Publisher from "./src/publisher/Publisher";
import DigitalGardenSettings from "./src/models/settings";
import { PublishStatusBar } from "./src/views/PublishStatusBar";
import { seedling } from "src/ui/suggest/constants";
import { PublicationCenter } from "src/views/PublicationCenter/PublicationCenter";
import PublishStatusManager from "src/publisher/PublishStatusManager";
import ObsidianFrontMatterEngine from "src/publishFile/ObsidianFrontMatterEngine";
import DigitalGardenSiteManager from "src/repositoryConnection/DigitalGardenSiteManager";
import { DigitalGardenSettingTab } from "./src/views/DigitalGardenSettingTab";
import Logger from "js-logger";
import { PublishFile } from "./src/publishFile/PublishFile";
import { FRONTMATTER_KEYS } from "./src/publishFile/FileMetaDataManager";
import { PublishPlatform } from "src/models/PublishPlatform";

// Process environment variables are provided through esbuild's define feature
// See esbuild.config.mjs

const defaultTheme = {
name: "Red Graphite",
Expand Down Expand Up @@ -55,9 +67,16 @@ const DEFAULT_SETTINGS: DigitalGardenSettings = {
styleSettingsBodyClasses: "",
pathRewriteRules: "",
customFilters: [],
publishPlatform: PublishPlatform.SelfHosted,

contentClassesKey: "dg-content-classes",

forestrySettings: {
forestryPageName: "",
apiKey: "",
baseUrl: "",
},

defaultNoteSettings: {
dgHomeLink: true,
dgPassFrontmatter: false,
Expand Down Expand Up @@ -159,7 +178,7 @@ export default class DigitalGarden extends Plugin {

this.addCommand({
id: "publish-note",
name: "Publish Single Note",
name: "Publish Active Note",
callback: async () => {
await this.publishSingleNote();
},
Expand Down Expand Up @@ -194,7 +213,7 @@ export default class DigitalGarden extends Plugin {

this.addCommand({
id: "publish-multiple-notes",
name: "Publish Multiple Notes",
name: "Publish All Notes Marked for Publish",
// TODO: move to publisher?
callback: async () => {
const statusBarItem = this.addStatusBarItem();
Expand Down Expand Up @@ -333,6 +352,14 @@ export default class DigitalGarden extends Plugin {
this.togglePublishFlag();
},
});

this.addCommand({
id: "dg-set-as-home-page",
name: "Set as Garden Home Page",
callback: async () => {
await this.setAsHomePage();
},
});
}

private getActiveFile(workspace: Workspace) {
Expand Down Expand Up @@ -434,12 +461,12 @@ export default class DigitalGarden extends Plugin {
return;
}

const engine = new ObsidianFrontMatterEngine(
this.app.vault,
this.app.metadataCache,
activeFile,
await this.app.fileManager.processFrontMatter(
activeFile as TFile,
(frontmatter) => {
frontmatter[FRONTMATTER_KEYS.PUBLISH] = value;
},
);
engine.set(FRONTMATTER_KEYS.PUBLISH, value).apply();
}
async togglePublishFlag() {
const activeFile = this.getActiveFile(this.app.workspace);
Expand All @@ -448,46 +475,204 @@ export default class DigitalGarden extends Plugin {
return;
}

const engine = new ObsidianFrontMatterEngine(
this.app.vault,
this.app.metadataCache,
activeFile,
await this.app.fileManager.processFrontMatter(
activeFile as TFile,
(frontmatter) => {
frontmatter[FRONTMATTER_KEYS.PUBLISH] =
!frontmatter[FRONTMATTER_KEYS.PUBLISH];
},
);

engine
.set(
FRONTMATTER_KEYS.PUBLISH,
!engine.get(FRONTMATTER_KEYS.PUBLISH),
)
.apply();
}

openPublishModal() {
if (!this.publishModal) {
const siteManager = new DigitalGardenSiteManager(
this.app.metadataCache,
this.settings,
);
async setAsHomePage() {
const activeFile = this.getActiveFile(this.app.workspace);

const publisher = new Publisher(
this.app.vault,
this.app.metadataCache,
this.settings,
);
if (!activeFile) {
return;
}

// Check if current file already has dg-home: true
const currentFileCache =
this.app.metadataCache.getFileCache(activeFile);

if (currentFileCache?.frontmatter?.[FRONTMATTER_KEYS.HOME]) {
new Notice("This note is already set as the garden home page.");

return;
}

// Find existing home pages
const existingHomePages: TFile[] = [];

for (const file of this.app.vault.getMarkdownFiles()) {
const cache = this.app.metadataCache.getFileCache(file);

const publishStatusManager = new PublishStatusManager(
siteManager,
publisher,
if (cache?.frontmatter?.[FRONTMATTER_KEYS.HOME]) {
existingHomePages.push(file);
}
}

if (existingHomePages.length === 0) {
// No existing home pages, just set this one
await this.app.fileManager.processFrontMatter(
activeFile as TFile,
(frontmatter) => {
frontmatter[FRONTMATTER_KEYS.HOME] = true;
frontmatter[FRONTMATTER_KEYS.PUBLISH] = true;
},
);

this.publishModal = new PublicationCenter(
this.app,
publishStatusManager,
publisher,
siteManager,
this.settings,
new Notice(
`${activeFile.basename} is now your garden's home page and has been marked for publishing.`,
);
} else {
// Show confirmation modal
new HomePageConfirmationModal(
this.app,
activeFile,
existingHomePages[0],
async (shouldUpdate) => {
if (shouldUpdate) {
// Remove dg-home from existing page
await this.app.fileManager.processFrontMatter(
existingHomePages[0],
(frontmatter) => {
delete frontmatter[FRONTMATTER_KEYS.HOME];
},
);

// Set dg-home on current page
await this.app.fileManager.processFrontMatter(
activeFile as TFile,
(frontmatter) => {
frontmatter[FRONTMATTER_KEYS.HOME] = true;
frontmatter[FRONTMATTER_KEYS.PUBLISH] = true;
},
);

new Notice(
`${activeFile.basename} is now your garden's home page and has been marked for publishing.`,
);
}
},
).open();
}
}

openPublishModal() {
const siteManager = new DigitalGardenSiteManager(
this.app.metadataCache,
this.settings,
);

const publisher = new Publisher(
this.app.vault,
this.app.metadataCache,
this.settings,
);

const publishStatusManager = new PublishStatusManager(
siteManager,
publisher,
);

this.publishModal = new PublicationCenter(
this.app,
publishStatusManager,
publisher,
siteManager,
this.settings,
);
this.publishModal.open();
}
}

class HomePageConfirmationModal extends Modal {
private onConfirm: (confirmed: boolean) => void;
private newHomeFile: TFile;
private existingHomeFile: TFile;

constructor(
app: App,
newHomeFile: TFile,
existingHomeFile: TFile,
onConfirm: (confirmed: boolean) => void,
) {
super(app);
this.newHomeFile = newHomeFile;
this.existingHomeFile = existingHomeFile;
this.onConfirm = onConfirm;
}

onOpen() {
const { contentEl } = this;
contentEl.createEl("h2", { text: "Replace Garden Home Page?" });

const messageDiv = contentEl.createDiv();

messageDiv.createEl("p", {
text: `${this.existingHomeFile.basename} is currently set as your garden home page.`,
});

messageDiv.createEl("p", {
text: `This will remove the home page setting from ${this.existingHomeFile.basename} and set ${this.newHomeFile.basename} as the new home page.`,
});

const warningDiv = contentEl.createDiv({
attr: {
style: "margin: 20px 0; padding: 12px; background: var(--background-secondary); border-radius: 4px;",
},
});

warningDiv.createEl("p", {
text: "⚠️ Important: Both notes should be published from the Publication Center to ensure your garden has a proper home page.",
attr: { style: "color: var(--text-warning); font-weight: bold;" },
});

const buttonContainer = contentEl.createDiv({
attr: {
style: "display: flex; gap: 10px; margin-top: 20px; justify-content: flex-end;",
},
});

const cancelButton = buttonContainer.createEl("button", {
text: "Cancel",
attr: {
style: "padding: 8px 16px; border-radius: 4px; cursor: pointer;",
},
});

cancelButton.onclick = () => {
this.onConfirm(false);
this.close();
};

const confirmButton = buttonContainer.createEl("button", {
text: "Replace Home Page",
attr: {
style: "padding: 8px 16px; border-radius: 4px; cursor: pointer; background: var(--interactive-accent); color: var(--text-on-accent);",
},
});

confirmButton.onclick = () => {
this.onConfirm(true);
this.close();
};

// Add keyboard support
this.scope.register([], "Enter", () => {
this.onConfirm(true);
this.close();
});

this.scope.register([], "Escape", () => {
this.onConfirm(false);
this.close();
});
}

onClose() {
const { contentEl } = this;
contentEl.empty();
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"esbuild-svelte": "^0.8.0",
"eslint": "^8.49.0",
"eslint-plugin-prettier": "^5.0.0",
"eslint-plugin-svelte": "^2.33.2",
"husky": "^8.0.3",
"jest": "^29.7.0",
"lint-staged": "^14.0.1",
Expand All @@ -45,7 +46,6 @@
"svelte-preprocess": "^5.0.4",
"ts-jest": "^29.1.1",
"tslib": "^2.6.2",
"eslint-plugin-svelte": "^2.33.2",
"typescript": "^5.2.2"
},
"dependencies": {
Expand Down
Loading