From d3c4602af5db9dcd084adff8eb6581287a566124 Mon Sep 17 00:00:00 2001 From: limina1 Date: Sat, 3 Jan 2026 17:00:32 -0500 Subject: [PATCH 01/16] fix: resolve CodeMirror duplicate instance error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove deprecated @codemirror/basic-setup@0.20.0 which pulled in conflicting 0.20.x versions alongside modern 6.x packages. Import basicSetup from 'codemirror' package instead. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- package-lock.json | 397 +------------------------ package.json | 1 - src/lib/components/ZettelEditor.svelte | 2 +- 3 files changed, 4 insertions(+), 396 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8d6981a..8677dca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,6 @@ "name": "alexandria", "version": "0.0.2", "dependencies": { - "@codemirror/basic-setup": "^0.20.0", "@codemirror/lang-markdown": "^6.3.4", "@codemirror/state": "^6.5.2", "@codemirror/theme-one-dark": "^6.1.3", @@ -175,97 +174,6 @@ "node": ">=6.9.0" } }, - "node_modules/@codemirror/autocomplete": { - "version": "0.20.3", - "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-0.20.3.tgz", - "integrity": "sha512-lYB+NPGP+LEzAudkWhLfMxhTrxtLILGl938w+RcFrGdrIc54A+UgmCoz+McE3IYRFp4xyQcL4uFJwo+93YdgHw==", - "license": "MIT", - "dependencies": { - "@codemirror/language": "^0.20.0", - "@codemirror/state": "^0.20.0", - "@codemirror/view": "^0.20.0", - "@lezer/common": "^0.16.0" - } - }, - "node_modules/@codemirror/autocomplete/node_modules/@codemirror/state": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-0.20.1.tgz", - "integrity": "sha512-ms0tlV5A02OK0pFvTtSUGMLkoarzh1F8mr6jy1cD7ucSC2X/VLHtQCxfhdSEGqTYlQF2hoZtmLv+amqhdgbwjQ==", - "license": "MIT" - }, - "node_modules/@codemirror/autocomplete/node_modules/@codemirror/view": { - "version": "0.20.7", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-0.20.7.tgz", - "integrity": "sha512-pqEPCb9QFTOtHgAH5XU/oVy9UR/Anj6r+tG5CRmkNVcqSKEPmBU05WtN/jxJCFZBXf6HumzWC9ydE4qstO3TxQ==", - "license": "MIT", - "dependencies": { - "@codemirror/state": "^0.20.0", - "style-mod": "^4.0.0", - "w3c-keyname": "^2.2.4" - } - }, - "node_modules/@codemirror/basic-setup": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@codemirror/basic-setup/-/basic-setup-0.20.0.tgz", - "integrity": "sha512-W/ERKMLErWkrVLyP5I8Yh8PXl4r+WFNkdYVSzkXYPQv2RMPSkWpr2BgggiSJ8AHF/q3GuApncDD8I4BZz65fyg==", - "deprecated": "In version 6.0, this package has been renamed to just 'codemirror'", - "license": "MIT", - "dependencies": { - "@codemirror/autocomplete": "^0.20.0", - "@codemirror/commands": "^0.20.0", - "@codemirror/language": "^0.20.0", - "@codemirror/lint": "^0.20.0", - "@codemirror/search": "^0.20.0", - "@codemirror/state": "^0.20.0", - "@codemirror/view": "^0.20.0" - } - }, - "node_modules/@codemirror/basic-setup/node_modules/@codemirror/state": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-0.20.1.tgz", - "integrity": "sha512-ms0tlV5A02OK0pFvTtSUGMLkoarzh1F8mr6jy1cD7ucSC2X/VLHtQCxfhdSEGqTYlQF2hoZtmLv+amqhdgbwjQ==", - "license": "MIT" - }, - "node_modules/@codemirror/basic-setup/node_modules/@codemirror/view": { - "version": "0.20.7", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-0.20.7.tgz", - "integrity": "sha512-pqEPCb9QFTOtHgAH5XU/oVy9UR/Anj6r+tG5CRmkNVcqSKEPmBU05WtN/jxJCFZBXf6HumzWC9ydE4qstO3TxQ==", - "license": "MIT", - "dependencies": { - "@codemirror/state": "^0.20.0", - "style-mod": "^4.0.0", - "w3c-keyname": "^2.2.4" - } - }, - "node_modules/@codemirror/commands": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-0.20.0.tgz", - "integrity": "sha512-v9L5NNVA+A9R6zaFvaTbxs30kc69F6BkOoiEbeFw4m4I0exmDEKBILN6mK+GksJtvTzGBxvhAPlVFTdQW8GB7Q==", - "license": "MIT", - "dependencies": { - "@codemirror/language": "^0.20.0", - "@codemirror/state": "^0.20.0", - "@codemirror/view": "^0.20.0", - "@lezer/common": "^0.16.0" - } - }, - "node_modules/@codemirror/commands/node_modules/@codemirror/state": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-0.20.1.tgz", - "integrity": "sha512-ms0tlV5A02OK0pFvTtSUGMLkoarzh1F8mr6jy1cD7ucSC2X/VLHtQCxfhdSEGqTYlQF2hoZtmLv+amqhdgbwjQ==", - "license": "MIT" - }, - "node_modules/@codemirror/commands/node_modules/@codemirror/view": { - "version": "0.20.7", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-0.20.7.tgz", - "integrity": "sha512-pqEPCb9QFTOtHgAH5XU/oVy9UR/Anj6r+tG5CRmkNVcqSKEPmBU05WtN/jxJCFZBXf6HumzWC9ydE4qstO3TxQ==", - "license": "MIT", - "dependencies": { - "@codemirror/state": "^0.20.0", - "style-mod": "^4.0.0", - "w3c-keyname": "^2.2.4" - } - }, "node_modules/@codemirror/lang-css": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.3.1.tgz", @@ -537,93 +445,6 @@ "@lezer/common": "^1.0.0" } }, - "node_modules/@codemirror/language": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-0.20.2.tgz", - "integrity": "sha512-WB3Bnuusw0xhVvhBocieYKwJm04SOk5bPoOEYksVHKHcGHFOaYaw+eZVxR4gIqMMcGzOIUil0FsCmFk8yrhHpw==", - "license": "MIT", - "dependencies": { - "@codemirror/state": "^0.20.0", - "@codemirror/view": "^0.20.0", - "@lezer/common": "^0.16.0", - "@lezer/highlight": "^0.16.0", - "@lezer/lr": "^0.16.0", - "style-mod": "^4.0.0" - } - }, - "node_modules/@codemirror/language/node_modules/@codemirror/state": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-0.20.1.tgz", - "integrity": "sha512-ms0tlV5A02OK0pFvTtSUGMLkoarzh1F8mr6jy1cD7ucSC2X/VLHtQCxfhdSEGqTYlQF2hoZtmLv+amqhdgbwjQ==", - "license": "MIT" - }, - "node_modules/@codemirror/language/node_modules/@codemirror/view": { - "version": "0.20.7", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-0.20.7.tgz", - "integrity": "sha512-pqEPCb9QFTOtHgAH5XU/oVy9UR/Anj6r+tG5CRmkNVcqSKEPmBU05WtN/jxJCFZBXf6HumzWC9ydE4qstO3TxQ==", - "license": "MIT", - "dependencies": { - "@codemirror/state": "^0.20.0", - "style-mod": "^4.0.0", - "w3c-keyname": "^2.2.4" - } - }, - "node_modules/@codemirror/lint": { - "version": "0.20.3", - "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-0.20.3.tgz", - "integrity": "sha512-06xUScbbspZ8mKoODQCEx6hz1bjaq9m8W8DxdycWARMiiX1wMtfCh/MoHpaL7ws/KUMwlsFFfp2qhm32oaCvVA==", - "license": "MIT", - "dependencies": { - "@codemirror/state": "^0.20.0", - "@codemirror/view": "^0.20.2", - "crelt": "^1.0.5" - } - }, - "node_modules/@codemirror/lint/node_modules/@codemirror/state": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-0.20.1.tgz", - "integrity": "sha512-ms0tlV5A02OK0pFvTtSUGMLkoarzh1F8mr6jy1cD7ucSC2X/VLHtQCxfhdSEGqTYlQF2hoZtmLv+amqhdgbwjQ==", - "license": "MIT" - }, - "node_modules/@codemirror/lint/node_modules/@codemirror/view": { - "version": "0.20.7", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-0.20.7.tgz", - "integrity": "sha512-pqEPCb9QFTOtHgAH5XU/oVy9UR/Anj6r+tG5CRmkNVcqSKEPmBU05WtN/jxJCFZBXf6HumzWC9ydE4qstO3TxQ==", - "license": "MIT", - "dependencies": { - "@codemirror/state": "^0.20.0", - "style-mod": "^4.0.0", - "w3c-keyname": "^2.2.4" - } - }, - "node_modules/@codemirror/search": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-0.20.1.tgz", - "integrity": "sha512-ROe6gRboQU5E4z6GAkNa2kxhXqsGNbeLEisbvzbOeB7nuDYXUZ70vGIgmqPu0tB+1M3F9yWk6W8k2vrFpJaD4Q==", - "license": "MIT", - "dependencies": { - "@codemirror/state": "^0.20.0", - "@codemirror/view": "^0.20.0", - "crelt": "^1.0.5" - } - }, - "node_modules/@codemirror/search/node_modules/@codemirror/state": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-0.20.1.tgz", - "integrity": "sha512-ms0tlV5A02OK0pFvTtSUGMLkoarzh1F8mr6jy1cD7ucSC2X/VLHtQCxfhdSEGqTYlQF2hoZtmLv+amqhdgbwjQ==", - "license": "MIT" - }, - "node_modules/@codemirror/search/node_modules/@codemirror/view": { - "version": "0.20.7", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-0.20.7.tgz", - "integrity": "sha512-pqEPCb9QFTOtHgAH5XU/oVy9UR/Anj6r+tG5CRmkNVcqSKEPmBU05WtN/jxJCFZBXf6HumzWC9ydE4qstO3TxQ==", - "license": "MIT", - "dependencies": { - "@codemirror/state": "^0.20.0", - "style-mod": "^4.0.0", - "w3c-keyname": "^2.2.4" - } - }, "node_modules/@codemirror/state": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.2.tgz", @@ -1415,12 +1236,6 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@lezer/common": { - "version": "0.16.1", - "resolved": "https://registry.npmjs.org/@lezer/common/-/common-0.16.1.tgz", - "integrity": "sha512-qPmG7YTZ6lATyTOAWf8vXE+iRrt1NJd4cm2nJHK+v7X9TsOF6+HtuU/ctaZy2RCrluxDb89hI6KWQ5LfQGQWuA==", - "license": "MIT" - }, "node_modules/@lezer/css": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.3.0.tgz", @@ -1456,15 +1271,6 @@ "@lezer/common": "^1.0.0" } }, - "node_modules/@lezer/highlight": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-0.16.0.tgz", - "integrity": "sha512-iE5f4flHlJ1g1clOStvXNLbORJoiW4Kytso6ubfYzHnaNo/eo5SKhxs4wv/rtvwZQeZrK3we8S9SyA7OGOoRKQ==", - "license": "MIT", - "dependencies": { - "@lezer/common": "^0.16.0" - } - }, "node_modules/@lezer/html": { "version": "1.3.10", "resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.10.tgz", @@ -1535,15 +1341,6 @@ "@lezer/common": "^1.0.0" } }, - "node_modules/@lezer/lr": { - "version": "0.16.3", - "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-0.16.3.tgz", - "integrity": "sha512-pau7um4eAw94BEuuShUIeQDTf3k4Wt6oIUOYxMmkZgDHdqtIcxWND4LRxi8nI9KuT4I1bXQv67BCapkxt7Ywqw==", - "license": "MIT", - "dependencies": { - "@lezer/common": "^0.16.0" - } - }, "node_modules/@lezer/markdown": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/@lezer/markdown/-/markdown-1.4.3.tgz", @@ -3258,35 +3055,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "license": "ISC", - "optional": true, - "peer": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/anymatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/apexcharts": { "version": "5.3.5", "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-5.3.5.tgz", @@ -3450,20 +3218,6 @@ "integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==", "license": "MIT" }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -3476,20 +3230,6 @@ "concat-map": "0.0.1" } }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/browserslist": { "version": "4.26.2", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.2.tgz", @@ -3667,46 +3407,6 @@ "node": ">= 16" } }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "license": "ISC", - "optional": true, - "peer": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/class-variance-authority": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", @@ -4978,20 +4678,6 @@ "node": ">=10" } }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -5162,6 +4848,7 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -5481,20 +5168,6 @@ "node": ">=12" } }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/is-core-module": { "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", @@ -5536,7 +5209,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "devOptional": true, + "dev": true, "license": "MIT", "peer": true, "engines": { @@ -5556,7 +5229,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "devOptional": true, + "dev": true, "license": "MIT", "peer": true, "dependencies": { @@ -5573,17 +5246,6 @@ "dev": true, "license": "MIT" }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=0.12.0" - } - }, "node_modules/is-promise": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", @@ -6220,17 +5882,6 @@ "dev": true, "license": "MIT" }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/normalize-range": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", @@ -7070,34 +6721,6 @@ "pify": "^2.3.0" } }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/readdirp/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -7661,20 +7284,6 @@ "node": ">=14.0.0" } }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, "node_modules/token-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-1.0.0.tgz", diff --git a/package.json b/package.json index b7ab411..7f15f7b 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,6 @@ "tokens": "node src/lib/theme/build-tokens.mjs" }, "dependencies": { - "@codemirror/basic-setup": "^0.20.0", "@codemirror/lang-markdown": "^6.3.4", "@codemirror/state": "^6.5.2", "@codemirror/theme-one-dark": "^6.1.3", diff --git a/src/lib/components/ZettelEditor.svelte b/src/lib/components/ZettelEditor.svelte index 6faecfe..a73923d 100644 --- a/src/lib/components/ZettelEditor.svelte +++ b/src/lib/components/ZettelEditor.svelte @@ -8,7 +8,7 @@ import { EditorState, StateField, StateEffect } from "@codemirror/state"; import { markdown } from "@codemirror/lang-markdown"; import { EditorView, Decoration, type DecorationSet } from "@codemirror/view"; - import { basicSetup } from "@codemirror/basic-setup"; + import { basicSetup } from "codemirror"; import { RangeSet } from "@codemirror/state"; import { onMount } from "svelte"; import { From bb50c53a4540f489e6195f11bdcb0cb164032124 Mon Sep 17 00:00:00 2001 From: Silberengel Date: Fri, 16 Jan 2026 18:16:24 +0100 Subject: [PATCH 02/16] update package-lock --- package-lock.json | 61 ++++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8677dca..8b72fcd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1462,13 +1462,12 @@ } }, "node_modules/@playwright/test": { - "version": "1.55.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.55.0.tgz", - "integrity": "sha512-04IXzPwHrW69XusN/SIdDdKZBzMfOT9UNT/YiJit/xpy2VuAoB8NHc8Aplb96zsWDddLnbkPL3TsmrS04ZU2xQ==", + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.57.0.tgz", + "integrity": "sha512-6TyEnHgd6SArQO8UO2OMTxshln3QMWBtPGrOCgs3wVEmQmwyuNtB10IZMfmYDE0riwNR1cu4q+pPcxMVtaG3TA==", "dev": true, - "license": "Apache-2.0", "dependencies": { - "playwright": "1.55.0" + "playwright": "1.57.0" }, "bin": { "playwright": "cli.js" @@ -2065,18 +2064,17 @@ } }, "node_modules/@sveltejs/kit": { - "version": "2.43.1", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.43.1.tgz", - "integrity": "sha512-H8eXW5TSziSvt9d5IJ5pPyWGhXQLdmq+17H9j7aofA/TsfSvG8ZIpTjObphFRNagfIyoFGyoB3lOzdsGHKiKpw==", + "version": "2.49.5", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.49.5.tgz", + "integrity": "sha512-dCYqelr2RVnWUuxc+Dk/dB/SjV/8JBndp1UovCyCZdIQezd8TRwFLNZctYkzgHxRJtaNvseCSRsuuHPeUgIN/A==", "dev": true, - "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/cookie": "^0.6.0", "acorn": "^8.14.1", "cookie": "^0.6.0", - "devalue": "^5.3.2", + "devalue": "^5.6.2", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", @@ -2095,11 +2093,15 @@ "@opentelemetry/api": "^1.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0", "svelte": "^4.0.0 || ^5.0.0-next.0", + "typescript": "^5.3.3", "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0" }, "peerDependenciesMeta": { "@opentelemetry/api": { "optional": true + }, + "typescript": { + "optional": true } } }, @@ -4119,11 +4121,10 @@ } }, "node_modules/devalue": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.3.2.tgz", - "integrity": "sha512-UDsjUbpQn9kvm68slnrs+mfxwFkIflOhkanmyabZ8zOYk8SMEIbJ3TK+88g70hSIeytu4y18f0z/hYHMTrXIWw==", - "dev": true, - "license": "MIT" + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.6.2.tgz", + "integrity": "sha512-nPRkjWzzDQlsejL1WVifk5rvcFi/y1onBRxjaFMjZeR9mFpqu2gmAZ9xUB9/IEanEP/vBtGeGganC/GO1fmufg==", + "dev": true }, "node_modules/dexie": { "version": "4.2.0", @@ -5328,11 +5329,10 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, - "license": "MIT", "peer": true, "dependencies": { "argparse": "^2.0.1" @@ -6176,13 +6176,12 @@ "license": "MIT" }, "node_modules/playwright": { - "version": "1.55.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.55.0.tgz", - "integrity": "sha512-sdCWStblvV1YU909Xqx0DhOjPZE4/5lJsIS84IfN9dAZfcl/CIZ5O8l3o0j7hPMjDvqoTF8ZUcc+i/GL5erstA==", + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.57.0.tgz", + "integrity": "sha512-ilYQj1s8sr2ppEJ2YVadYBN0Mb3mdo9J0wQ+UuDhzYqURwSoW4n1Xs5vs7ORwgDGmyEh33tRMeS8KhdkMoLXQw==", "dev": true, - "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.55.0" + "playwright-core": "1.57.0" }, "bin": { "playwright": "cli.js" @@ -6195,11 +6194,10 @@ } }, "node_modules/playwright-core": { - "version": "1.55.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.55.0.tgz", - "integrity": "sha512-GvZs4vU3U5ro2nZpeiwyb0zuFaqb9sUiAJuyrWpcGouD8y9/HLgGbNRjIph7zU9D3hnPaisMl9zG9CgFi/biIg==", + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.57.0.tgz", + "integrity": "sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ==", "dev": true, - "license": "Apache-2.0", "bin": { "playwright-core": "cli.js" }, @@ -7434,11 +7432,10 @@ "license": "MIT" }, "node_modules/vite": { - "version": "6.3.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.6.tgz", - "integrity": "sha512-0msEVHJEScQbhkbVTb/4iHZdJ6SXp/AvxL2sjwYQFfBqleHtnCqv1J3sa9zbWz/6kW1m9Tfzn92vW+kZ1WV6QA==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", + "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", "dev": true, - "license": "MIT", "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", From 813a856e2403bfa2649a6e4d9168ecec550fa654 Mon Sep 17 00:00:00 2001 From: Silberengel Date: Fri, 16 Jan 2026 18:16:52 +0100 Subject: [PATCH 03/16] silence window-scope error --- src/lib/utils/network_detection.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/lib/utils/network_detection.ts b/src/lib/utils/network_detection.ts index c1821b8..5358441 100644 --- a/src/lib/utils/network_detection.ts +++ b/src/lib/utils/network_detection.ts @@ -25,8 +25,9 @@ const NETWORK_ENDPOINTS = [ export async function isNetworkOnline(): Promise { for (const endpoint of NETWORK_ENDPOINTS) { try { - // Use a simple fetch without HEAD method to avoid CORS issues - await fetch(endpoint, { + // Use window.fetch explicitly for client-side network detection + // This is intentionally client-side code, not a SvelteKit load function + await window.fetch(endpoint, { method: "GET", cache: "no-cache", signal: AbortSignal.timeout(3000), @@ -56,7 +57,9 @@ export async function testNetworkSpeed(): Promise { for (const endpoint of NETWORK_ENDPOINTS) { try { - await fetch(endpoint, { + // Use window.fetch explicitly for client-side network detection + // This is intentionally client-side code, not a SvelteKit load function + await window.fetch(endpoint, { method: "GET", cache: "no-cache", signal: AbortSignal.timeout(5000), From cd2489b8651cc6cf5b2f5ecc00c616da43b6200c Mon Sep 17 00:00:00 2001 From: Silberengel Date: Fri, 16 Jan 2026 18:46:49 +0100 Subject: [PATCH 04/16] add orly and newsroom localhost port remove nostr.band get rid of individual relay settings --- TECHNIQUE-create-test-highlights.md | 3 +-- check-publication-structure.js | 2 +- create-test-comments.js | 10 ++++------ create-test-highlights.js | 10 ++++------ src/lib/components/util/Profile.svelte | 2 ++ src/lib/consts.ts | 4 ++-- src/lib/utils/mockCommentData.ts | 15 +++++++++------ src/lib/utils/mockHighlightData.ts | 8 ++++++-- src/lib/utils/websocket_utils.ts | 5 +++-- 9 files changed, 32 insertions(+), 27 deletions(-) diff --git a/TECHNIQUE-create-test-highlights.md b/TECHNIQUE-create-test-highlights.md index d3e1bbd..b628b0e 100644 --- a/TECHNIQUE-create-test-highlights.md +++ b/TECHNIQUE-create-test-highlights.md @@ -47,7 +47,7 @@ const rootAddress = `${data.kind}:${data.pubkey}:${data.identifier}`; console.log("\nRoot Address:", rootAddress); // Fetch the index event to see what sections it references -const relay = "wss://relay.nostr.band"; +const relay = "wss://thecitadel.nostr1.com"; async function fetchPublication() { return new Promise((resolve, reject) => { @@ -172,7 +172,6 @@ const sections = [ // Relays to publish to (matching HighlightLayer's relay list) const relays = [ "wss://relay.damus.io", - "wss://relay.nostr.band", "wss://nostr.wine", ]; diff --git a/check-publication-structure.js b/check-publication-structure.js index 3948a9f..cb5e2ea 100644 --- a/check-publication-structure.js +++ b/check-publication-structure.js @@ -13,7 +13,7 @@ const rootAddress = `${data.kind}:${data.pubkey}:${data.identifier}`; console.log("\nRoot Address:", rootAddress); // Fetch the index event to see what sections it references -const relay = "wss://relay.nostr.band"; +const relay = "wss://thecitadel.nostr1.com"; async function fetchPublication() { return new Promise((resolve, reject) => { diff --git a/create-test-comments.js b/create-test-comments.js index bae0f1b..99b38eb 100644 --- a/create-test-comments.js +++ b/create-test-comments.js @@ -1,5 +1,7 @@ import { finalizeEvent, generateSecretKey, getPublicKey } from "nostr-tools"; import WebSocket from "ws"; +import { activeInboxRelays } from "./src/lib/ndk.ts"; +import { secondaryRelays } from "./src/lib/consts.ts"; // Test user keys (generate fresh ones) const testUserKey = generateSecretKey(); @@ -25,12 +27,8 @@ const sections = [ `30041:${publicationPubkey}:the-persistent-escape-of-knowledge`, ]; -// Relays to publish to (matching CommentLayer's relay list) -const relays = [ - "wss://relay.damus.io", - "wss://relay.nostr.band", - "wss://nostr.wine", -]; +// Relays to publish to - should match src/lib/consts.ts relay constants +const relays = [...secondaryRelays, ...activeInboxRelays]; // Test comments to create const testComments = [ diff --git a/create-test-highlights.js b/create-test-highlights.js index b5acc66..4b97965 100644 --- a/create-test-highlights.js +++ b/create-test-highlights.js @@ -1,5 +1,7 @@ import { finalizeEvent, generateSecretKey, getPublicKey } from "nostr-tools"; import WebSocket from "ws"; +import { activeInboxRelays } from "./src/lib/ndk.ts"; +import { secondaryRelays } from "./src/lib/consts.ts"; // Test user keys (generate fresh ones) const testUserKey = generateSecretKey(); @@ -25,12 +27,8 @@ const sections = [ `30041:${publicationPubkey}:the-persistent-escape-of-knowledge`, ]; -// Relays to publish to (matching HighlightLayer's relay list) -const relays = [ - "wss://relay.damus.io", - "wss://relay.nostr.band", - "wss://nostr.wine", -]; +// Relays to publish to - should match src/lib/consts.ts relay constants +const relays = [...secondaryRelays, ...activeInboxRelays]; // Test highlights to create // AI-NOTE: Kind 9802 highlight events contain the actual highlighted text in .content diff --git a/src/lib/components/util/Profile.svelte b/src/lib/components/util/Profile.svelte index 9d3f1fd..8a57edd 100644 --- a/src/lib/components/util/Profile.svelte +++ b/src/lib/components/util/Profile.svelte @@ -298,6 +298,8 @@ isLoadingExtension = false; try { const ndk = new NDK(); + // AI-NOTE: relay.nsec.app is the specific relay for Amber/NIP-46 signer service + // This is service-specific and not a general-purpose relay, so it remains hard-coded const relay = "wss://relay.nsec.app"; const localNsec = localStorage.getItem("amber/nsec") ?? diff --git a/src/lib/consts.ts b/src/lib/consts.ts index e0224b1..a3de298 100644 --- a/src/lib/consts.ts +++ b/src/lib/consts.ts @@ -11,12 +11,11 @@ export const communityRelays = [ ]; export const searchRelays = [ - "wss://profiles.nostr1.com", + "wss://thecitadel.nostr1.com", "wss://aggr.nostr.land", "wss://relay.noswhere.com", "wss://nostr.wine", "wss://relay.damus.io", - "wss://relay.nostr.band", "wss://freelay.sovbit.host", ]; @@ -46,6 +45,7 @@ export const localRelays: string[] = [ "ws://localhost:8080", "ws://localhost:4869", "ws://localhost:3334", + "ws://localhost:7777" ]; export enum FeedType { diff --git a/src/lib/utils/mockCommentData.ts b/src/lib/utils/mockCommentData.ts index 4bdb19a..835c195 100644 --- a/src/lib/utils/mockCommentData.ts +++ b/src/lib/utils/mockCommentData.ts @@ -1,5 +1,6 @@ import { NDKEvent } from "@nostr-dev-kit/ndk"; import type NDK from "@nostr-dev-kit/ndk"; +import { secondaryRelays } from "../consts.ts"; /** * Generate mock comment data for testing comment UI and threading @@ -49,18 +50,20 @@ function createMockComment( replyToId?: string, replyToAuthor?: string, ): any { + // Use first search relay from constants instead of hard-coded relay + const relayUrl = secondaryRelays[0] || "wss://thecitadel.nostr1.com"; const tags: string[][] = [ - ["A", targetAddress, "wss://relay.damus.io", pubkey], + ["A", targetAddress, relayUrl, pubkey], ["K", "30041"], - ["P", pubkey, "wss://relay.damus.io"], - ["a", targetAddress, "wss://relay.damus.io"], + ["P", pubkey, relayUrl], + ["a", targetAddress, relayUrl], ["k", "30041"], - ["p", pubkey, "wss://relay.damus.io"], + ["p", pubkey, relayUrl], ]; if (replyToId && replyToAuthor) { - tags.push(["e", replyToId, "wss://relay.damus.io", "reply"]); - tags.push(["p", replyToAuthor, "wss://relay.damus.io"]); + tags.push(["e", replyToId, relayUrl, "reply"]); + tags.push(["p", replyToAuthor, relayUrl]); } // Return a plain object that matches NDKEvent structure diff --git a/src/lib/utils/mockHighlightData.ts b/src/lib/utils/mockHighlightData.ts index 950762e..b76ce10 100644 --- a/src/lib/utils/mockHighlightData.ts +++ b/src/lib/utils/mockHighlightData.ts @@ -1,3 +1,5 @@ +import { secondaryRelays } from "../consts.ts"; + /** * Generate mock highlight data (kind 9802) for testing highlight UI * Creates realistic highlight events with context and optional annotations @@ -76,10 +78,12 @@ function createMockHighlight( offsetStart?: number, offsetEnd?: number, ): any { + // Use first search relay from constants instead of hard-coded relay + const relayUrl = secondaryRelays[0] || "wss://thecitadel.nostr1.com"; const tags: string[][] = [ - ["a", targetAddress, "wss://relay.damus.io"], + ["a", targetAddress, relayUrl], ["context", context], - ["p", authorPubkey, "wss://relay.damus.io", "author"], + ["p", authorPubkey, relayUrl, "author"], ]; // Add optional annotation diff --git a/src/lib/utils/websocket_utils.ts b/src/lib/utils/websocket_utils.ts index 43d2025..b5ab129 100644 --- a/src/lib/utils/websocket_utils.ts +++ b/src/lib/utils/websocket_utils.ts @@ -92,8 +92,9 @@ export async function fetchNostrEvent( const { searchRelays, secondaryRelays } = await import("../consts.ts"); availableRelays = [...searchRelays, ...secondaryRelays]; - if (availableRelays.length === 0) { - availableRelays = ["wss://thecitadel.nostr1.com"]; + // Final fallback: use first secondary relay if all else fails + if (availableRelays.length === 0 && secondaryRelays.length > 0) { + availableRelays = [secondaryRelays[1]]; } } From 5407bcd7fc4347a9ba18628488499ab4f80e3e95 Mon Sep 17 00:00:00 2001 From: Silberengel Date: Fri, 16 Jan 2026 18:50:37 +0100 Subject: [PATCH 05/16] suppress no filters to merge error from NDK --- src/lib/utils/subscription_search.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/lib/utils/subscription_search.ts b/src/lib/utils/subscription_search.ts index 48e3bf1..a5dcd10 100644 --- a/src/lib/utils/subscription_search.ts +++ b/src/lib/utils/subscription_search.ts @@ -339,6 +339,18 @@ export async function searchBySubscription( ndk, ); console.log("subscription_search: Created search filter:", searchFilter); + + // AI-NOTE: Validate filter to prevent "No filters to merge!" error from NDK + if (!searchFilter.filter || Object.keys(searchFilter.filter).length === 0) { + console.error( + "subscription_search: Invalid or empty filter created:", + searchFilter, + ); + throw new Error( + `Failed to create valid filter for search type: ${searchType}`, + ); + } + const primaryRelaySet = createPrimaryRelaySet(searchType, ndk); console.log( "subscription_search: Created primary relay set with", @@ -1076,6 +1088,15 @@ function searchOtherRelaysInBackground( Array.from(ndk.pool.relays.values()).map((r: any) => r.url), ); + // AI-NOTE: Validate filter before subscribing to prevent "No filters to merge!" error + if (!searchFilter.filter || Object.keys(searchFilter.filter).length === 0) { + console.warn( + "subscription_search: Invalid or empty filter, skipping background search", + searchFilter, + ); + return Promise.resolve(createSearchResult(searchState, searchType, "")); + } + // Subscribe to events from other relays const sub = ndk.subscribe( searchFilter.filter, From beea9d1b26d5736859e049028f6ecf38974ac566 Mon Sep 17 00:00:00 2001 From: Silberengel Date: Fri, 16 Jan 2026 19:09:21 +0100 Subject: [PATCH 06/16] Increase the number of publications returned --- src/lib/components/publications/PublicationFeed.svelte | 3 ++- src/lib/utils/search_constants.ts | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/lib/components/publications/PublicationFeed.svelte b/src/lib/components/publications/PublicationFeed.svelte index 2d51c95..0d0873b 100644 --- a/src/lib/components/publications/PublicationFeed.svelte +++ b/src/lib/components/publications/PublicationFeed.svelte @@ -1,5 +1,6 @@ diff --git a/src/lib/components/publications/Publication.svelte b/src/lib/components/publications/Publication.svelte index 092af49..cab9d41 100644 --- a/src/lib/components/publications/Publication.svelte +++ b/src/lib/components/publications/Publication.svelte @@ -699,15 +699,29 @@ function toggleComments() { commentsVisible = !commentsVisible; + + // AI-NOTE: When toggling comments on, ensure CommentLayer fetches comments + // The effect in CommentLayer should handle this, but we can also trigger a refresh + if (commentsVisible && commentLayerRef) { + console.debug("[Publication] Comments toggled on, triggering refresh"); + // Small delay to ensure addresses are available + setTimeout(() => { + if (commentLayerRef && commentsVisible) { + commentLayerRef.refresh(); + } + }, 100); + } } function handleCommentPosted() { - // Refresh the comment layer after a short delay to allow relay indexing + // AI-NOTE: Refresh the comment layer after a delay to allow relay indexing + // Increased delay to 3 seconds to give relays more time to index the new comment setTimeout(() => { if (commentLayerRef) { + console.debug("[Publication] Refreshing CommentLayer after comment posted"); commentLayerRef.refresh(); } - }, 500); + }, 3000); } async function submitArticleComment() { @@ -1476,6 +1490,7 @@ {toc} allComments={comments} {commentsVisible} + onCommentPosted={handleCommentPosted} ref={(el) => onPublicationSectionMounted(el, address)} /> {:else} @@ -1630,5 +1645,7 @@ diff --git a/src/lib/components/publications/PublicationSection.svelte b/src/lib/components/publications/PublicationSection.svelte index fadabe7..7fc2775 100644 --- a/src/lib/components/publications/PublicationSection.svelte +++ b/src/lib/components/publications/PublicationSection.svelte @@ -28,6 +28,7 @@ commentsVisible = true, publicationTitle, isFirstSection = false, + onCommentPosted, }: { address: string; rootAddress: string; @@ -39,19 +40,84 @@ commentsVisible?: boolean; publicationTitle?: string; isFirstSection?: boolean; + onCommentPosted?: () => void; } = $props(); const asciidoctor: Asciidoctor = getContext("asciidoctor"); const ndk: NDK = getContext("ndk"); // Filter comments for this section - let sectionComments = $derived( - allComments.filter((comment) => { - // Check if comment targets this section via #a tag - const aTag = comment.tags.find((t) => t[0] === "a"); - return aTag && aTag[1] === address; - }), - ); + // AI-NOTE: NIP-22: Uppercase tags (A, E, I, K, P) point to root scope (section/publication) + // Lowercase tags (a, e, i, k, p) point to parent item (comment being replied to) + // All comments scoped to this section will have uppercase A tag matching section address + let sectionComments = $derived.by(() => { + // Step 1: Find all comments scoped to this section (have uppercase A tag matching section address) + const directComments = allComments.filter((comment) => { + // NIP-22: Look for uppercase A tag (root scope) + const rootATag = comment.tags.find((t) => t[0] === "A"); + const matches = rootATag && rootATag[1] === address; + + // AI-NOTE: Debug logging to help diagnose comment filtering issues + if (rootATag) { + console.debug("[PublicationSection] Comment filtering:", { + sectionAddress: address, + commentRootATag: rootATag[1], + matches, + commentId: comment.id?.substring(0, 8), + }); + } + + return matches; + }); + + // Step 2: Build a set of comment IDs that match this section (for efficient lookup) + const matchingCommentIds = new Set( + directComments.map(c => c.id?.toLowerCase()).filter(Boolean) + ); + + // Step 3: Recursively find all replies to matching comments + // NIP-22: Replies have lowercase e tag pointing to parent comment ID + // They also have uppercase A tag matching section address (same root scope) + const allMatchingComments = new Set(directComments); + let foundNewReplies = true; + + // Keep iterating until we find no new replies (handles nested replies) + while (foundNewReplies) { + foundNewReplies = false; + + for (const comment of allComments) { + // Skip if already included + if (allMatchingComments.has(comment)) { + continue; + } + + // NIP-22: Check if this comment is scoped to this section (uppercase A tag) + const rootATag = comment.tags.find((t) => t[0] === "A"); + if (!rootATag || rootATag[1] !== address) { + // Not scoped to this section, skip + continue; + } + + // NIP-22: Check if this is a reply (has lowercase e tag pointing to a matching comment) + const lowercaseETags = comment.tags.filter(t => t[0] === "e"); + for (const eTag of lowercaseETags) { + const parentId = eTag[1]?.toLowerCase(); + if (parentId && matchingCommentIds.has(parentId)) { + // This is a reply to a matching comment - include it + allMatchingComments.add(comment); + matchingCommentIds.add(comment.id?.toLowerCase() || ""); + foundNewReplies = true; + console.debug(`[PublicationSection] Found reply ${comment.id?.substring(0, 8)} to matching comment ${parentId.substring(0, 8)} (NIP-22)`); + break; // Found a match, no need to check other e tags + } + } + } + } + + const filtered = Array.from(allMatchingComments); + console.debug(`[PublicationSection] Filtered ${filtered.length} comments (${directComments.length} direct, ${filtered.length - directComments.length} replies) for section ${address} from ${allComments.length} total comments`); + return filtered; + }); let leafEvent: Promise = $derived.by( async () => await publicationTree.getEvent(address), @@ -227,7 +293,8 @@ -
+ +
{/if} {/await} @@ -283,9 +351,11 @@
- + +