Skip to content

Commit

Permalink
feat!: refactor for cloud Flowershow version
Browse files Browse the repository at this point in the history
## Changes

- replace `isDraft: true` with Obsidian-publish-compatible `publish: false`,
- adjust notes syncing to mirror the vault 1-to-1 with the repo (no longer adds content to `/content` and assets to `/public`),
- improve publish manager modal styles a bit and add: link to repo, refresh button and settings button
- adjust usage instructions in `README.md` (almost the same as added here: datopian/datahub-next#689 to `/obsidian-quickstart`)
- add short info at the top with a link to the `/obsidian-quickstart?ref=obsidian` (with ref param that we can use for analytics, but needs to be tested if this is enough) 
- replace the old, temporary icon 🌱 with our Flowershow icon in Obsidian ribbon
- add notice at the top of the `README.md` about breaking change
- bump major version from `1.0.0` to `2.0.0`
  • Loading branch information
olayway authored Feb 14, 2025
1 parent f25c795 commit f01c549
Show file tree
Hide file tree
Showing 10 changed files with 170 additions and 84 deletions.
69 changes: 45 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,41 +1,59 @@
# 🌷 Obsidian Flowershow Plugin

> ⚠️ **IMPORTANT**: This plugin is no longer compatible with self-hosted Flowershow sites. It is now exclusively used as a tool for Flowershow Cloud. [Sign up for Flowershow Cloud here](https://cloud.flowershow.app)
Obsidian Flowershow plugin for publishing with [Flowershow](https://github.com/datopian/flowershow) direct from your Obsidian vault.

## Docs
## Getting Started

### Initial Setup

1. Firstly, you will need a GitHub account. If you don't have it yet, create one [here](https://github.com/signup).
2. You'll also need a Vercel account. You can sign up using your GitHub account [here](https://vercel.com/signup)
3. Open [this repo](https://github.com/datopian/flowershow), and click the blue "Deploy" button under "Quick clone and deploy" section. This will open Vercel's "Create Git Repository" page. Pick a name for your site's repository and click "Create", to create a copy of the template repository in your GitHub account and deploy it to Vercel.
4. Now you need to create a personal access token on GitHub, so that the plugin can add/delete notes to/from the repo. Go to [this page](https://github.com/settings/tokens/new?scopes=repo) while logged in to GitHub. The correct settings should already be applied. If you don't want to generate this every few months, choose the "No expiration" option. Click the "Generate token" button, and copy the token you are presented with on the next page.
5. In Obsidian open Flowershow plugin settings. Fill in your GitHub username, the name of the repo with your notes which you created in step 3. Lastly paste the token you created in step 4.
6. Now, let's publish your first note! Create a new note in Obsidian.
7. Open your command pallete by pressing CTRL+P on Windows/Linux (CMD+P on Mac) and find the "Flowershow: Publish Single Note" command. Press enter.
8. Go to your site's URL which you should find on [Vercel](https://vercel.com/dashboard). If nothing shows up yet, wait a minute and refresh. Your Flowershow site with the note you just created should now be up and running.
1. First, you will need a GitHub account. If you don't have one yet, create one [here](https://github.com/signup).

2. Go to our [GitHub template](https://github.com/datopian/flowershow) and click on the green "Use this template" button. Then, select "Create a new repository" option from the dropdown.

3. Give your repository a name and choose whether you want to keep it private or public.

4. Once the repository is created, click on the "Create New Site" button in your dashboard and choose the newly created GitHub repository to use as a base for your site.

5. Wait for your repository to finish syncing with the content. Once done, you can click on the "Visit" button to see your created site.

6. Now you need to create a personal access token on GitHub, so that the plugin can sync your notes with the repository. Go to [this page](https://github.com/settings/tokens/new?scopes=repo) while logged in to GitHub. The correct settings should already be applied. If you don't want to generate this every few months, choose the "No expiration" option. Click the "Generate token" button, and copy the token you are presented with on the next page.

### Publishing Your Notes

1. Install and enable the Obsidian Flowershow plugin in your vault.

Congratulations, you now have your own Flowershow site, hosted free of charge!
You can now start adding links as you usually would in Obisidan, with double square brackets like this: [[Some Other Note]], to the note that you just published. You can also link to a specific header by using the syntax [[Some Other Note#A Header]]. Remember to also publish the notes your are linking to as this will not happen automatically.
2. Open the plugin settings and provide:
- Your GitHub username
- The name of the GitHub repository you created earlier
- The personal access token you generated

### Commands
3. Close the settings and click on the Flowershow icon in the ribbon.

* `Flowershow: Publish Single Note` - Publishes the current note to your Flowershow site.
* `Flowershow: Publish All Notes` - Publishes all notes in your vault to your Flowershow site.
4. Click on "Sync all" to fully synchronize your site's content with your vault.

### Ribbon commands
Done! Your notes are ready to be shared with the world! 💐

After installing the plugin, you'll see a new icon added to your Obsidian ribbon - 🌱.
Clicking on it will pull up the Publication Status panel, which includes:
You can now start adding links as you usually would in Obsidian, with double square brackets like this: [[Some Other Note]]. You can also link to a specific header by using the syntax [[Some Other Note#A Header]].

### Publication Status Panel

The Flowershow icon in your ribbon opens the Publication Status panel, which shows:

* **Published**: the total number of notes that has been published to your Flowershow site
* **Changed**: the total number of __published__ notes that has been edited locally (+ button to publish them)
* **Unpublished**: the total number of new notes in your Obsidian vault, that has not yet been published to your site (+ button to publish them)
* **Deleted**: the total number of notes that has been deleted from your Obsidian vault, but are still published on your site (+ button to unpublish them)

### Available Commands

* `Flowershow: Publish Single Note` - Publishes the current note to your Flowershow site
* `Flowershow: Publish All Notes` - Publishes all notes in your vault to your Flowershow site

### Frontmatter settings

* `isDraft` - Set to `true` to keep the note unpublished from your Flowershow site (or unpublish it if it was published before). Default: `false`.
* `publish` - Set to `false` to keep the note unpublished from your Flowershow site (or unpublish it if it was published before).

## Development

Expand All @@ -44,15 +62,19 @@ Clicking on it will pull up the Publication Status panel, which includes:
1. Clone the repository.
2. Run `npm i` to install dependencies.
3. Run `npm run build`.
4. Create symlinks to the `main.js`, `manifest.json`, and `styles.css` files in your Obsidian plugins folder:
4. Create the plugins directory in your Obsidian vault if it doesn't exist:
```sh
mkdir -p /path/to/obsidian-vault/.obsidian/plugins/flowershow
```
5. Create symlinks to the `main.js`, `manifest.json`, and `styles.css` files in your Obsidian plugins folder:

``` sh
```sh
ln -s /path/to/obsidian-flowershow/main.js /path/to/obsidian-vault/.obsidian/plugins/flowershow/main.js
ln -s /path/to/obsidian-flowershow/manifest.json /path/to/obsidian-vault/.obsidian/plugins/flowershow/manifest.json
ln -s /path/to/obsidian-flowershow/styles.css /path/to/obsidian-vault/.obsidian/plugins/flowershow/styles.css
```

5. Reload Obsidian, go to Settings > Community Plugins, and enable the plugin.
6. Reload Obsidian, go to Settings > Community Plugins, and enable the plugin.

### Rebuild on change

Expand All @@ -66,13 +88,13 @@ If you want true hot reloading, i.e. without needing to disable/enable the plugi
- download the .zip file from the latest release
- extract the .zip file into your Obsidian vault's `.obsidian/plugins` folder
- go to Settings > Community Plugins and enable the plugin
2. Instead of creating symlinks like in step 4 above, copy the plugin files directly into your Obsidian vault's `.obsidian/plugins` folder:
2. Instead of creating symlinks like in step 4 above, copy/clone the plugin project directly into your Obsidian vault's `.obsidian/plugins` folder:

``` sh
mv /path/to/obsidian-flowershow /path/to/obsidian-vault/.obsidian/plugins/
```

3. Run `npm run dev` to start the server.
3. Run `npm i && npm run dev` in the plugin folder to start the development server.

Now, whenever you make any changes to the source code, two things will happen:
1. The plugin will be rebuilt automatically.
Expand All @@ -81,4 +103,3 @@ Now, whenever you make any changes to the source code, two things will happen:
## Shoutout

Big thanks to [Ole Eskild Steensen](https://github.com/oleeskild) for [his obsidian-digital-garden plugin](https://github.com/oleeskild/obsidian-digital-garden/tree/main) which inspired us and we got to build on.

4 changes: 2 additions & 2 deletions main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import PublishStatusModal from 'src/PublishStatusModal';
import SettingView from 'src/SettingView';
import SiteManager, { ISiteManager } from 'src/SiteManager';

import { seedling } from 'src/constants';
import { flowershowIcon } from 'src/constants';


export default class Flowershow extends Plugin {
Expand All @@ -32,7 +32,7 @@ export default class Flowershow extends Plugin {
this.addSettingTab(new FlowershowSettingTab(this.app, this));
await this.addCommands();

addIcon('flowershow-icon', seedling);
addIcon('flowershow-icon', flowershowIcon);
this.addRibbonIcon("flowershow-icon", "Publish with Flowershow", async () => {
this.openPublishStatusModal();
});
Expand Down
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "flowershow",
"name": "Flowershow",
"version": "1.0.0",
"version": "2.0.0",
"minAppVersion": "0.0.1",
"description": "Publish with Flowershow directly from your Obsidian vault.",
"author": "Rufus Pollock",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "flowershow",
"version": "1.0.0",
"version": "2.0.0",
"description": "Obsidian Flowershow plugin for publishing with flowershow direct from your obsidian vault.",
"main": "main.js",
"scripts": {
Expand Down
85 changes: 76 additions & 9 deletions src/PublishStatusModal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ export interface IPublishStatusModal {

export default class PublishStatusModal implements IPublishStatusModal {
private modal: Modal;
// private settings: FlowershowSettings;
private settings: FlowershowSettings;
private publishStatusManager: IPublishStatusManager;
private app: App;
private publisher: IPublisher;
private publishStatus: PublishStatus;

Expand All @@ -28,7 +29,8 @@ export default class PublishStatusModal implements IPublishStatusModal {

constructor(app: App, publishStatusManager: IPublishStatusManager, publisher: IPublisher, settings: FlowershowSettings) {
this.modal = new Modal(app);
// this.settings = settings;
this.app = app;
this.settings = settings;
this.publishStatusManager = publishStatusManager;
this.publisher = publisher;

Expand All @@ -42,15 +44,68 @@ export default class PublishStatusModal implements IPublishStatusModal {

// DONE
private async initialize() {
this.modal.titleEl.innerText = "🌷 Flowershow";
this.modal.contentEl.addClass("digital-garden-publish-status-view");
this.modal.contentEl.createEl("h2", { text: "Publication Status" });

// Add GitHub repository header with link
const headerEl = this.modal.contentEl.createEl("div", { cls: "publish-header" });
headerEl.style.display = "flex";
headerEl.style.justifyContent = "space-between";
headerEl.style.alignItems = "center";
headerEl.style.marginBottom = "20px";
headerEl.style.padding = "10px";
headerEl.style.borderBottom = "1px solid var(--background-modifier-border)";

const headerLeft = headerEl.createEl("div");
const repoUrl = `https://github.com/${this.settings.githubUserName}/${this.settings.githubRepo}`;
const headerText = headerLeft.createEl("p", { cls: "publish-header-text" });
headerText.style.margin = "0";
headerText.setText("Publishing to ");

const link = headerText.createEl("a", {
text: `${this.settings.githubUserName}/${this.settings.githubRepo}`,
href: repoUrl
});
link.style.color = "var(--text-accent)";
link.style.textDecoration = "none";

// Add icons container
const iconsContainer = headerEl.createEl("div");
iconsContainer.style.display = "flex";
iconsContainer.style.gap = "8px";

// Add sync icon
const syncIcon = iconsContainer.createEl("div", { cls: "clickable-icon" });
syncIcon.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-refresh-cw"><path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"></path><path d="M21 3v5h-5"></path><path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"></path><path d="M3 21v-5h5"></path></svg>`;
syncIcon.style.cursor = "pointer";
syncIcon.addEventListener("click", async () => {
this.progressContainer.innerText = `⌛ Refreshing status...`;
await this.refreshStatus();
this.progressContainer.innerText = `✅ Status refreshed`;
setTimeout(() => {
this.progressContainer.innerText = "";
}, 2000);
});

// Add settings icon
const settingsIcon = iconsContainer.createEl("div", { cls: "clickable-icon" });
settingsIcon.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-settings"><path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"></path><circle cx="12" cy="12" r="3"></circle></svg>`;
settingsIcon.style.cursor = "pointer";
settingsIcon.addEventListener("click", () => {
this.modal.close();
// @ts-ignore
this.app.setting?.open();
// @ts-ignore
this.app.setting?.openTabById("obsidian-flowershow");
});

this.progressContainer = this.modal.contentEl.createEl("div");
this.progressContainer.addClass("progress-container");
this.progressContainer.style.padding = "0 10px";
this.progressContainer.style.marginBottom = "8px";

[this.publishedCounter, this.publishedList] = this.createSection("Published", null, null);
[this.changedCounter, this.changedList] = this.createSection("Changed", "Update changed notes", async () => this.publishChangedNotes());
[this.unpublishedCounter, this.unpublishedList] = this.createSection("Unpublished", "Publish unpublished notes", async () => this.publishUnpublishedNotes());
[this.changedCounter, this.changedList] = this.createSection("Changed", "Update notes", async () => this.publishChangedNotes());
[this.unpublishedCounter, this.unpublishedList] = this.createSection("Unpublished", "Publish notes", async () => this.publishUnpublishedNotes());
[this.deletedCounter, this.deletedList] = this.createSection("Deleted", "Delete notes from site", async () => this.unpublishNotes());

this.modal.onOpen = () => this.initView();
Expand All @@ -61,10 +116,18 @@ export default class PublishStatusModal implements IPublishStatusModal {
private createSection(title: string, buttonText: string, buttonCallback: () => Promise<void>): Array<HTMLElement> {
const headerContainer = this.modal.contentEl.createEl("div");
headerContainer.addClass("header-container");
headerContainer.style.marginBottom = "8px"; // Add smaller margin between sections
const collapsableList = this.modal.contentEl.createEl("ul");
collapsableList.style.padding = "4px 0 4px 20px";
collapsableList.style.margin = "0";
const titleContainer = headerContainer.createEl("div");
titleContainer.addClass("title-container");
const toggleHeader = titleContainer.createEl("h3", { text: `➕️ ${title}`, attr: { class: "collapsable collapsed" } });
const toggleHeader = titleContainer.createEl("h3", {
text: `▶ ${title}`,
attr: { class: "collapsable collapsed" },
cls: "small-chevron"
});
toggleHeader.style.fontSize = "1em";
const counter = titleContainer.createEl("span");
counter.addClass("count");

Expand All @@ -83,12 +146,12 @@ export default class PublishStatusModal implements IPublishStatusModal {

headerContainer.onClickEvent(() => {
if (collapsableList.isShown()) {
toggleHeader.textContent = `➕️ ${title}`;
toggleHeader.textContent = ` ${title}`;
collapsableList.hide();
toggleHeader.removeClass("open");
toggleHeader.addClass("collapsed");
} else {
toggleHeader.textContent = ` ${title}`;
toggleHeader.textContent = ` ${title}`;
collapsableList.show()
toggleHeader.removeClass("collapsed");
toggleHeader.addClass("open");
Expand All @@ -109,6 +172,7 @@ export default class PublishStatusModal implements IPublishStatusModal {
publishedNotes.forEach(file => {
const li = document.createElement('li');
li.textContent = file.path;
li.style.padding = "2px 0";
this.publishedList.appendChild(li);
});

Expand All @@ -118,6 +182,7 @@ export default class PublishStatusModal implements IPublishStatusModal {
unpublishedNotes.forEach(file => {
const li = document.createElement('li');
li.textContent = file.path;
li.style.padding = "2px 0";
this.unpublishedList.appendChild(li);
});

Expand All @@ -127,6 +192,7 @@ export default class PublishStatusModal implements IPublishStatusModal {
changedNotes.forEach(file => {
const li = document.createElement('li');
li.textContent = file.path;
li.style.padding = "2px 0";
this.changedList.appendChild(li);
});

Expand All @@ -136,6 +202,7 @@ export default class PublishStatusModal implements IPublishStatusModal {
deletedNotePaths.forEach(path => {
const li = document.createElement('li');
li.textContent = path;
li.style.padding = "2px 0";
this.deletedList.appendChild(li);
});
}
Expand Down
Loading

0 comments on commit f01c549

Please sign in to comment.