Ignis settings tabs
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
BIN
images/ignis.png
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 38 KiB |
BIN
images/ignis.webp
Normal file
|
After Width: | Height: | Size: 15 KiB |
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"id": "ignis-bridge",
|
"id": "ignis-bridge",
|
||||||
"name": "Ignis Bridge",
|
"name": "Ignis Bridge",
|
||||||
"version": "1.0.0",
|
"version": "0.6.4",
|
||||||
"minAppVersion": "1.0.0",
|
"minAppVersion": "1.12.4",
|
||||||
"description": "Upload files from your device to the vault",
|
"description": "Additional Ignis specific functionality and ignis plugin management.",
|
||||||
"author": "Ignis",
|
"author": "Nystik",
|
||||||
"authorUrl": "https://github.com/Nystik-gh/ignis",
|
"authorUrl": "https://github.com/Nystik-gh/ignis",
|
||||||
"isDesktopOnly": false
|
"isDesktopOnly": false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,158 @@
|
|||||||
const { Setting } = require("obsidian");
|
const { Setting } = require("obsidian");
|
||||||
|
|
||||||
function display(containerEl) {
|
const GITHUB_URL = "https://github.com/Nystik-gh/ignis";
|
||||||
containerEl.createEl("h2", { text: "Ignis General Settings" });
|
const GITHUB_API_LATEST =
|
||||||
|
"https://api.github.com/repos/Nystik-gh/ignis/releases/latest";
|
||||||
|
|
||||||
new Setting(containerEl)
|
function getVersion(app) {
|
||||||
.setName("Example toggle")
|
try {
|
||||||
.setDesc("This is a test toggle to prove the Setting API works.")
|
const manifest = app.plugins.getPlugin("ignis-bridge")?.manifest;
|
||||||
.addToggle((toggle) => {
|
return manifest?.version || "unknown";
|
||||||
toggle.setValue(false);
|
} catch {
|
||||||
toggle.onChange((value) => {
|
return "unknown";
|
||||||
console.log("[ignis] Toggle:", value);
|
}
|
||||||
});
|
}
|
||||||
});
|
|
||||||
|
async function checkForUpdate(currentVersion) {
|
||||||
|
try {
|
||||||
|
const res = await fetch(GITHUB_API_LATEST);
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
const latest = data.tag_name?.replace(/^v/, "");
|
||||||
|
|
||||||
|
if (latest && latest !== currentVersion) {
|
||||||
|
return latest;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function display(containerEl, app) {
|
||||||
|
const version = getVersion(app);
|
||||||
|
|
||||||
|
const header = containerEl.createDiv("ignis-header");
|
||||||
|
|
||||||
|
const logo = header.createEl("img", {
|
||||||
|
cls: "ignis-header-logo",
|
||||||
|
attr: { src: "/assets/ignis.webp", alt: "Ignis" },
|
||||||
|
});
|
||||||
|
|
||||||
|
const info = header.createDiv("ignis-header-info");
|
||||||
|
info.createEl("div", { text: "Ignis", cls: "ignis-header-title" });
|
||||||
|
info.createEl("div", {
|
||||||
|
text: "Obsidian server bridge",
|
||||||
|
cls: "ignis-header-subtitle",
|
||||||
|
});
|
||||||
|
|
||||||
|
const right = header.createDiv("ignis-header-right");
|
||||||
|
|
||||||
|
const versionCol = right.createDiv("ignis-header-version-col");
|
||||||
|
versionCol.createEl("span", {
|
||||||
|
text: `Version ${version}`,
|
||||||
|
cls: "ignis-header-version",
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateIndicator = versionCol.createEl("span", {
|
||||||
|
text: "Checking...",
|
||||||
|
cls: "ignis-update-indicator",
|
||||||
|
});
|
||||||
|
|
||||||
|
const githubLink = right.createEl("a", {
|
||||||
|
cls: "ignis-github-link",
|
||||||
|
href: GITHUB_URL,
|
||||||
|
attr: { target: "_blank", "aria-label": "GitHub" },
|
||||||
|
});
|
||||||
|
|
||||||
|
const githubIcon = githubLink.createEl("img", {
|
||||||
|
cls: "ignis-github-icon",
|
||||||
|
attr: { src: "/assets/github.svg", alt: "GitHub" },
|
||||||
|
});
|
||||||
|
|
||||||
|
checkForUpdate(version).then((latestVersion) => {
|
||||||
|
if (latestVersion) {
|
||||||
|
updateIndicator.textContent = `v${latestVersion} available`;
|
||||||
|
updateIndicator.addClass("ignis-update-available");
|
||||||
|
} else {
|
||||||
|
updateIndicator.textContent = "Up to date";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
addServerStatus(containerEl);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getWsStatus() {
|
||||||
|
const ws = window.__ignisWs;
|
||||||
|
|
||||||
|
if (!ws) {
|
||||||
|
return "disconnected";
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (ws.readyState) {
|
||||||
|
case WebSocket.CONNECTING:
|
||||||
|
return "connecting";
|
||||||
|
case WebSocket.OPEN:
|
||||||
|
return "connected";
|
||||||
|
case WebSocket.CLOSING:
|
||||||
|
case WebSocket.CLOSED:
|
||||||
|
return "disconnected";
|
||||||
|
default:
|
||||||
|
return "disconnected";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function statusLabel(status) {
|
||||||
|
switch (status) {
|
||||||
|
case "connected":
|
||||||
|
return "Connected";
|
||||||
|
case "connecting":
|
||||||
|
return "Connecting...";
|
||||||
|
case "disconnected":
|
||||||
|
return "Disconnected";
|
||||||
|
default:
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addServerStatus(containerEl) {
|
||||||
|
const status = getWsStatus();
|
||||||
|
|
||||||
|
const setting = new Setting(containerEl).setName("Server status");
|
||||||
|
|
||||||
|
const dotEl = setting.controlEl.createEl("span", {
|
||||||
|
cls: `ignis-status-dot ignis-status-${status}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
const labelEl = setting.controlEl.createEl("span", {
|
||||||
|
text: statusLabel(status),
|
||||||
|
cls: "ignis-status-label",
|
||||||
|
});
|
||||||
|
|
||||||
|
const update = () => {
|
||||||
|
const s = getWsStatus();
|
||||||
|
dotEl.className = `ignis-status-dot ignis-status-${s}`;
|
||||||
|
labelEl.textContent = statusLabel(s);
|
||||||
|
};
|
||||||
|
|
||||||
|
const pollInterval = setInterval(update, 3000);
|
||||||
|
|
||||||
|
const observer = new MutationObserver(() => {
|
||||||
|
if (!containerEl.isConnected) {
|
||||||
|
clearInterval(pollInterval);
|
||||||
|
observer.disconnect();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(containerEl.parentElement || document.body, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { display };
|
module.exports = { display };
|
||||||
|
|||||||
@@ -41,30 +41,44 @@ function createTab(id, name, displayFn, app) {
|
|||||||
return tab;
|
return tab;
|
||||||
}
|
}
|
||||||
|
|
||||||
function injectIgnisSettings(setting, app) {
|
function createGroup(name) {
|
||||||
const group = document.createElement("div");
|
const group = document.createElement("div");
|
||||||
group.className = "vertical-tab-header-group";
|
group.className = "vertical-tab-header-group";
|
||||||
|
|
||||||
const title = document.createElement("div");
|
const title = document.createElement("div");
|
||||||
title.className = "vertical-tab-header-group-title";
|
title.className = "vertical-tab-header-group-title";
|
||||||
title.textContent = "Ignis";
|
title.textContent = name;
|
||||||
group.appendChild(title);
|
group.appendChild(title);
|
||||||
|
|
||||||
const items = document.createElement("div");
|
const items = document.createElement("div");
|
||||||
items.className = "vertical-tab-header-group-items";
|
items.className = "vertical-tab-header-group-items";
|
||||||
group.appendChild(items);
|
group.appendChild(items);
|
||||||
|
|
||||||
|
return { group, items };
|
||||||
|
}
|
||||||
|
|
||||||
|
function injectIgnisSettings(setting, app) {
|
||||||
|
const ignis = createGroup("Ignis");
|
||||||
|
|
||||||
const tabs = [
|
const tabs = [
|
||||||
createTab("ignis-general", "General", generalTab.display, app),
|
createTab("ignis-general", "General", generalTab.display, app),
|
||||||
createTab("ignis-server-plugins", "Server Plugins", serverPluginsTab.display, app),
|
createTab(
|
||||||
|
"ignis-core-plugins",
|
||||||
|
"Core plugins",
|
||||||
|
serverPluginsTab.display,
|
||||||
|
app,
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const tab of tabs) {
|
for (const tab of tabs) {
|
||||||
tab.navEl = createNavEl(tab, setting);
|
tab.navEl = createNavEl(tab, setting);
|
||||||
items.appendChild(tab.navEl);
|
ignis.items.appendChild(tab.navEl);
|
||||||
}
|
}
|
||||||
|
|
||||||
setting.tabHeadersEl.appendChild(group);
|
setting.tabHeadersEl.appendChild(ignis.group);
|
||||||
|
|
||||||
|
const corePlugins = createGroup("Ignis Core Plugins");
|
||||||
|
setting.tabHeadersEl.appendChild(corePlugins.group);
|
||||||
}
|
}
|
||||||
|
|
||||||
function patchSettingsModal(plugin) {
|
function patchSettingsModal(plugin) {
|
||||||
|
|||||||
@@ -48,7 +48,14 @@ async function activateBundledPlugin(bundledPluginId, enable, app) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function display(containerEl, app) {
|
function display(containerEl, app) {
|
||||||
containerEl.createEl("h2", { text: "Server Plugins" });
|
containerEl.createEl("h2", { text: "Ignis Core Plugins" });
|
||||||
|
|
||||||
|
const descEl = containerEl.createEl("p", {
|
||||||
|
text:
|
||||||
|
"Ignis plugins extend server functionality and run alongside your vaults. " +
|
||||||
|
"They are separate from Obsidian's built-in plugins.",
|
||||||
|
cls: "ignis-plugins-description",
|
||||||
|
});
|
||||||
|
|
||||||
const loadingEl = containerEl.createEl("p", { text: "Loading plugins..." });
|
const loadingEl = containerEl.createEl("p", { text: "Loading plugins..." });
|
||||||
|
|
||||||
|
|||||||
113
plugin/styles.css
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
.ignis-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
padding: 16px 0 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
border-bottom: 1px solid var(--background-modifier-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ignis-header-logo {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ignis-header-info {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ignis-header-title {
|
||||||
|
font-size: var(--font-ui-large);
|
||||||
|
font-weight: var(--font-semibold);
|
||||||
|
line-height: 1.2;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ignis-header-subtitle {
|
||||||
|
font-size: var(--font-ui-small);
|
||||||
|
color: var(--text-muted);
|
||||||
|
line-height: 1.2;
|
||||||
|
margin: 4px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ignis-header-right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ignis-header-version-col {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-end;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ignis-header-version {
|
||||||
|
font-size: var(--font-ui-small);
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ignis-update-indicator {
|
||||||
|
font-size: var(--font-ui-smaller);
|
||||||
|
color: var(--text-faint);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ignis-update-indicator.ignis-update-available {
|
||||||
|
color: var(--text-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ignis-github-link {
|
||||||
|
color: var(--text-muted);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ignis-github-link:hover {
|
||||||
|
color: var(--text-normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ignis-github-link:hover .ignis-github-icon {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ignis-github-icon {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ignis-status-dot {
|
||||||
|
display: inline-block;
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ignis-status-connected {
|
||||||
|
background-color: var(--color-green);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ignis-status-connecting {
|
||||||
|
background-color: var(--color-yellow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ignis-status-disconnected {
|
||||||
|
background-color: var(--color-red);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ignis-status-label {
|
||||||
|
font-size: var(--font-ui-small);
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ignis-plugins-description {
|
||||||
|
padding: 0 16px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
font-size: var(--font-ui-small);
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
1
server/assets/github.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 98 96" fill="#a0a0a0"><path fill-rule="evenodd" clip-rule="evenodd" d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z"/></svg>
|
||||||
|
After Width: | Height: | Size: 984 B |
BIN
server/assets/ignis.webp
Normal file
|
After Width: | Height: | Size: 15 KiB |
@@ -50,6 +50,8 @@ const vaultRoutes = require("./routes/vault");
|
|||||||
const proxyRoutes = require("./routes/proxy");
|
const proxyRoutes = require("./routes/proxy");
|
||||||
const versionRoutes = require("./routes/version");
|
const versionRoutes = require("./routes/version");
|
||||||
|
|
||||||
|
app.use("/assets", express.static(path.join(__dirname, "assets")));
|
||||||
|
|
||||||
app.use("/api/fs", fsRoutes);
|
app.use("/api/fs", fsRoutes);
|
||||||
app.use("/api/vault", vaultRoutes);
|
app.use("/api/vault", vaultRoutes);
|
||||||
app.use("/api/proxy", proxyRoutes);
|
app.use("/api/proxy", proxyRoutes);
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export function createWatcherClient(metadataCache, contentCache, fsWatch) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
ws = new WebSocket(url);
|
ws = new WebSocket(url);
|
||||||
|
window.__ignisWs = ws;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("[watcher] Failed to create WebSocket:", e);
|
console.error("[watcher] Failed to create WebSocket:", e);
|
||||||
scheduleReconnect();
|
scheduleReconnect();
|
||||||
|
|||||||