From 408eb7fb545d5f08ac69ada1db74c77f127952b7 Mon Sep 17 00:00:00 2001 From: Nystik <236107-Nystik@users.noreply.gitlab.com> Date: Wed, 18 Mar 2026 18:28:07 +0100 Subject: [PATCH] add svelte UI --- build-ui.js | 17 + package-lock.json | 249 +++++++++++++- package.json | 15 +- scripts/entrypoint.sh | 1 + scripts/patch-obsidian.js | 5 +- server/routes/vault.js | 34 ++ shims/ui/vault-manager.js | 156 +-------- ui/components/display/ListItem.svelte | 104 ++++++ ui/components/input/Button.svelte | 94 +++++ ui/components/input/SearchInput.svelte | 64 ++++ ui/components/layout/ConfirmDialog.svelte | 85 +++++ ui/components/layout/Modal.svelte | 130 +++++++ ui/components/layout/PromptDialog.svelte | 115 +++++++ ui/components/menu/PopoverMenu.svelte | 101 ++++++ ui/index.js | 1 + ui/views/VaultManager.svelte | 395 ++++++++++++++++++++++ 16 files changed, 1403 insertions(+), 163 deletions(-) create mode 100644 build-ui.js create mode 100644 ui/components/display/ListItem.svelte create mode 100644 ui/components/input/Button.svelte create mode 100644 ui/components/input/SearchInput.svelte create mode 100644 ui/components/layout/ConfirmDialog.svelte create mode 100644 ui/components/layout/Modal.svelte create mode 100644 ui/components/layout/PromptDialog.svelte create mode 100644 ui/components/menu/PopoverMenu.svelte create mode 100644 ui/index.js create mode 100644 ui/views/VaultManager.svelte diff --git a/build-ui.js b/build-ui.js new file mode 100644 index 0000000..e4e41bb --- /dev/null +++ b/build-ui.js @@ -0,0 +1,17 @@ +const esbuild = require("esbuild"); +const sveltePlugin = require("esbuild-svelte"); +const path = require("path"); + +esbuild.build({ + entryPoints: [path.join(__dirname, "ui", "index.js")], + bundle: true, + outfile: path.join(__dirname, "dist", "ignis-ui.js"), + format: "iife", + globalName: "IgnisUI", + platform: "browser", + target: ["chrome90"], + mainFields: ["svelte", "browser", "module", "main"], + conditions: ["svelte", "browser"], + plugins: [sveltePlugin({ compilerOptions: { css: "injected" } })], + logLevel: "info", +}).catch(() => process.exit(1)); diff --git a/package-lock.json b/package-lock.json index a9dd3b2..c853069 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "obsidian-bridge", + "name": "ignis", "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "obsidian-bridge", + "name": "ignis", "version": "0.1.0", "dependencies": { "chokidar": "^3.6.0", @@ -15,7 +15,24 @@ }, "devDependencies": { "esbuild": "^0.20.0", - "path-browserify": "^1.0.1" + "esbuild-svelte": "^0.9.4", + "lucide-svelte": "^0.577.0", + "path-browserify": "^1.0.1", + "svelte": "^4.2.20" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" } }, "node_modules/@esbuild/aix-ppc64": { @@ -409,6 +426,52 @@ "node": ">=12" } }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -422,6 +485,19 @@ "node": ">= 0.6" } }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -435,12 +511,32 @@ "node": ">= 8" } }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "license": "MIT" }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -551,6 +647,20 @@ "fsevents": "~2.3.2" } }, + "node_modules/code-red": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz", + "integrity": "sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15", + "@types/estree": "^1.0.1", + "acorn": "^8.10.0", + "estree-walker": "^3.0.3", + "periscopic": "^3.1.0" + } + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -604,6 +714,20 @@ "url": "https://opencollective.com/express" } }, + "node_modules/css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -730,12 +854,39 @@ "@esbuild/win32-x64": "0.20.2" } }, + "node_modules/esbuild-svelte": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/esbuild-svelte/-/esbuild-svelte-0.9.4.tgz", + "integrity": "sha512-v/a0GjkKN06nal2QLluxjk2GXsei3fdtjIuHRa6pVnri5rQBZ6pj4a2WwjLfRojgRsLwDHl4xSeZ1BeUHsqQrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.19" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "esbuild": ">=0.17.0", + "svelte": ">=4.2.1 <6" + } + }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "license": "MIT" }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -1036,6 +1187,43 @@ "node": ">=0.12.0" } }, + "node_modules/is-reference": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", + "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.6" + } + }, + "node_modules/locate-character": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", + "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lucide-svelte": { + "version": "0.577.0", + "resolved": "https://registry.npmjs.org/lucide-svelte/-/lucide-svelte-0.577.0.tgz", + "integrity": "sha512-0i88o57KsaHWnc80J57fY99CWzlZsSdtH5kKjLUJa7z8dum/9/AbINNLzJ7NiRFUdOgMnfAmJt8jFbW2zeC5qQ==", + "dev": true, + "license": "ISC", + "peerDependencies": { + "svelte": "^3 || ^4 || ^5.0.0-next.42" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -1045,6 +1233,13 @@ "node": ">= 0.4" } }, + "node_modules/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "dev": true, + "license": "CC0-1.0" + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -1184,6 +1379,18 @@ "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", "license": "MIT" }, + "node_modules/periscopic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", + "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^3.0.0", + "is-reference": "^3.0.0" + } + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -1409,6 +1616,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", @@ -1418,6 +1635,32 @@ "node": ">= 0.8" } }, + "node_modules/svelte": { + "version": "4.2.20", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.20.tgz", + "integrity": "sha512-eeEgGc2DtiUil5ANdtd8vPwt9AgaMdnuUFnPft9F5oMvU/FHu5IHFic+p1dR/UOB7XU2mX2yHW+NcTch4DCh5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.1", + "@jridgewell/sourcemap-codec": "^1.4.15", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/estree": "^1.0.1", + "acorn": "^8.9.0", + "aria-query": "^5.3.0", + "axobject-query": "^4.0.0", + "code-red": "^1.0.3", + "css-tree": "^2.3.1", + "estree-walker": "^3.0.3", + "is-reference": "^3.0.1", + "locate-character": "^3.0.0", + "magic-string": "^0.30.4", + "periscopic": "^3.1.0" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", diff --git a/package.json b/package.json index 85628d0..3199247 100644 --- a/package.json +++ b/package.json @@ -4,18 +4,23 @@ "private": true, "description": "An Electron shim and server bridge for running Obsidian in a browser.", "scripts": { + "build:ui": "node build-ui.js", "build:shims": "node build.js", + "build": "npm run build:ui && npm run build:shims", "dev:server": "node server/index.js", - "dev": "npm run build:shims && npm run dev:server" + "dev": "npm run build && npm run dev:server" }, "dependencies": { - "express": "^4.21.0", - "ws": "^8.16.0", "chokidar": "^3.6.0", - "cors": "^2.8.5" + "cors": "^2.8.5", + "express": "^4.21.0", + "ws": "^8.16.0" }, "devDependencies": { "esbuild": "^0.20.0", - "path-browserify": "^1.0.1" + "esbuild-svelte": "^0.9.4", + "lucide-svelte": "^0.577.0", + "path-browserify": "^1.0.1", + "svelte": "^4.2.20" } } diff --git a/scripts/entrypoint.sh b/scripts/entrypoint.sh index ef71ce3..ce524b3 100644 --- a/scripts/entrypoint.sh +++ b/scripts/entrypoint.sh @@ -23,6 +23,7 @@ if [ ! -f "$OBSIDIAN_DIR/index.html" ]; then echo "[ignis] Patching..." node /app/scripts/patch-obsidian.js "$OBSIDIAN_DIR" + cp /app/dist/ignis-ui.js "$OBSIDIAN_DIR/ignis-ui.js" cp /app/dist/shim-loader.js "$OBSIDIAN_DIR/shim-loader.js" cp /app/images/favicon.png "$OBSIDIAN_DIR/favicon.png" diff --git a/scripts/patch-obsidian.js b/scripts/patch-obsidian.js index 22c6a13..e7dddc9 100644 --- a/scripts/patch-obsidian.js +++ b/scripts/patch-obsidian.js @@ -33,10 +33,11 @@ function patchHtml(filePath) { ' \n', ); - // Inject shim-loader before the first \n' + + '\n' + + '\n' + ' + + +
+ {#if $$slots.icon} +
+ +
+ {/if} + +
+ {#if $$slots.default} + + {:else} + {primary} + {#if secondary} + {secondary} + {/if} + {/if} +
+ + {#if $$slots.action} +
+ +
+ {/if} +
+ + diff --git a/ui/components/input/Button.svelte b/ui/components/input/Button.svelte new file mode 100644 index 0000000..e1453af --- /dev/null +++ b/ui/components/input/Button.svelte @@ -0,0 +1,94 @@ + + + + + diff --git a/ui/components/input/SearchInput.svelte b/ui/components/input/SearchInput.svelte new file mode 100644 index 0000000..f734ed1 --- /dev/null +++ b/ui/components/input/SearchInput.svelte @@ -0,0 +1,64 @@ + + +
+ + + + +
+ + diff --git a/ui/components/layout/ConfirmDialog.svelte b/ui/components/layout/ConfirmDialog.svelte new file mode 100644 index 0000000..3f3f043 --- /dev/null +++ b/ui/components/layout/ConfirmDialog.svelte @@ -0,0 +1,85 @@ + + + + + + + +
+

{message}

+ {#if description} +

{description}

+ {/if} +
+ + + + +
+ + diff --git a/ui/components/layout/Modal.svelte b/ui/components/layout/Modal.svelte new file mode 100644 index 0000000..2929b78 --- /dev/null +++ b/ui/components/layout/Modal.svelte @@ -0,0 +1,130 @@ + + + + + + diff --git a/ui/components/layout/PromptDialog.svelte b/ui/components/layout/PromptDialog.svelte new file mode 100644 index 0000000..3e5ac11 --- /dev/null +++ b/ui/components/layout/PromptDialog.svelte @@ -0,0 +1,115 @@ + + + + + + + +
+ + + +
+ + + + +
+ + diff --git a/ui/components/menu/PopoverMenu.svelte b/ui/components/menu/PopoverMenu.svelte new file mode 100644 index 0000000..bf45239 --- /dev/null +++ b/ui/components/menu/PopoverMenu.svelte @@ -0,0 +1,101 @@ + + +
+ + + {#if open} +
+ {#each items as item} + + {/each} +
+ {/if} +
+ + diff --git a/ui/index.js b/ui/index.js new file mode 100644 index 0000000..1a15fc5 --- /dev/null +++ b/ui/index.js @@ -0,0 +1 @@ +export { default as VaultManager } from "./views/VaultManager.svelte"; diff --git a/ui/views/VaultManager.svelte b/ui/views/VaultManager.svelte new file mode 100644 index 0000000..b548c0d --- /dev/null +++ b/ui/views/VaultManager.svelte @@ -0,0 +1,395 @@ + + + + + + + +
+

Vaults

+
+ { + searchQuery = e.detail; + }} + /> +
+
+ +
+
+ {#if vaults.length === 0} +
No vaults yet. Create one below.
+ {:else if filteredVaults.length === 0} +
No vaults match your search.
+ {:else} + {#each filteredVaults as vault (vault.id)} + openVault(vault)} + > + + + + + + {vault.name} + {#if vault.id === currentVaultId} + (active) + + {/if} + + {vault.path} + + + toggleMenu(vault.id)} + on:select={(e) => onMenuSelect(vault, e.detail)} + /> + + + {/each} + {/if} +
+
+ + + + +
+ +{#if activeDialog === "create"} + + + + + + + + +{/if} + +{#if activeDialog === "rename"} + + + + + + + + +{/if} + +{#if activeDialog === "delete" && targetVault} + + + + + + + + +{/if} + +