feat: node view redesign — single-scroll, no tabs
Ports the full node view redesign from ra-h. Replaces the 4-tab layout (Desc/Notes/Edges/Source) with a single-scroll document view. - New FocusPanel: frontmatter rows, collapse toggle, NodeSearchModal, TipTap SourceEditor, section headers with extending rules - NodeSearchModal: new full-screen portal modal for creating connections - SourceEditor: TipTap WYSIWYG editor (new file) - SourceReader: removed header chrome, added onContentClick prop - Source formatters: inherit font, 15px/1.7, no max-width centering - DimensionTags: subtle dashed + button replaces green pill - autoEmbedQueue: recover nodes stuck in 'chunking' state on startup - edges: fix extra parameter in getNodeConnectionsSQLite Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
0d2c499b70
commit
6677b1b64b
Generated
+826
-3
@@ -13,6 +13,8 @@
|
||||
"@langchain/core": "^1.0.1",
|
||||
"@langchain/textsplitters": "^1.0.1",
|
||||
"@modelcontextprotocol/sdk": "^1.25.3",
|
||||
"@tiptap/react": "^3.20.4",
|
||||
"@tiptap/starter-kit": "^3.20.4",
|
||||
"@xyflow/react": "^12.10.0",
|
||||
"ai": "^6.0.27",
|
||||
"better-sqlite3": "^12.2.0",
|
||||
@@ -27,6 +29,7 @@
|
||||
"react-dom": "^19.0.1",
|
||||
"react-markdown": "^10.1.0",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"tiptap-markdown": "^0.9.0",
|
||||
"youtube-transcript": "^1.2.1",
|
||||
"youtube-transcript-plus": "^1.1.1",
|
||||
"zod": "^3.25.76"
|
||||
@@ -667,6 +670,34 @@
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/core": {
|
||||
"version": "1.7.5",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz",
|
||||
"integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@floating-ui/utils": "^0.2.11"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/dom": {
|
||||
"version": "1.7.6",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz",
|
||||
"integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@floating-ui/core": "^1.7.5",
|
||||
"@floating-ui/utils": "^0.2.11"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/utils": {
|
||||
"version": "0.2.11",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz",
|
||||
"integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/@hono/node-server": {
|
||||
"version": "1.19.9",
|
||||
"resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.9.tgz",
|
||||
@@ -1691,6 +1722,12 @@
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@remirror/core-constants": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@remirror/core-constants/-/core-constants-3.0.0.tgz",
|
||||
"integrity": "sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.55.1.tgz",
|
||||
@@ -2076,6 +2113,440 @@
|
||||
"tslib": "^2.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/core": {
|
||||
"version": "3.20.4",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.20.4.tgz",
|
||||
"integrity": "sha512-3i/DG89TFY/b34T5P+j35UcjYuB5d3+9K8u6qID+iUqNPiza015HPIZLuPfE5elNwVdV3EXIoPo0LLeBLgXXAg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/pm": "^3.20.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-blockquote": {
|
||||
"version": "3.20.4",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-3.20.4.tgz",
|
||||
"integrity": "sha512-9sskyyhYj2oKat//lyZVXCp9YrPt4oJAZnGHYWXS0xlskjsLElrfKKlM4vpbhGss3VrhQRoEGqWLnIaJYPF1zw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.20.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-bold": {
|
||||
"version": "3.20.4",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-3.20.4.tgz",
|
||||
"integrity": "sha512-Md7/mNAeJCY+VLJc8JRGI+8XkVPKiOGB1NgqQPdh3aYtxXQDChQOZoJEQl6TuudDxZ85bLZB67NjZlx3jo8/0g==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.20.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-bubble-menu": {
|
||||
"version": "3.20.4",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-3.20.4.tgz",
|
||||
"integrity": "sha512-EXywPlI8wjPcAb8ozymgVhjtMjFrnhtoyNTy8ZcObdpUi5CdO9j892Y7aPbKe5hLhlDpvJk7rMfir4FFKEmfng==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@floating-ui/dom": "^1.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.20.4",
|
||||
"@tiptap/pm": "^3.20.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-bullet-list": {
|
||||
"version": "3.20.4",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-3.20.4.tgz",
|
||||
"integrity": "sha512-1RTGrur1EKoxfnLZ3M6xeNj8GITAz74jH2DHGcjLsd2Xr7Q7BozGaIq6GkkvKguMwbI1zCOxTHFCpUETXAIQQA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/extension-list": "^3.20.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-code": {
|
||||
"version": "3.20.4",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-3.20.4.tgz",
|
||||
"integrity": "sha512-7j8Hi964bH1SZ9oLdZC1fkqWz27mliSDV7M8lmL/M14+Qw42D/VOAKS4Aw9OCFtHMlTsjLR6qsoVxL8Lpkt6NA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.20.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-code-block": {
|
||||
"version": "3.20.4",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-3.20.4.tgz",
|
||||
"integrity": "sha512-Zlw3FrXTy01+o1yISeX/LC+iJeHA+ym602bMXGmtA6lyl7QSOSO7WExweJ6xeJGhbCjldwT5al6fkRAs8iGJZg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.20.4",
|
||||
"@tiptap/pm": "^3.20.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-document": {
|
||||
"version": "3.20.4",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-3.20.4.tgz",
|
||||
"integrity": "sha512-zF1CIFVLt8MfSpWWnPwtGyxPOsT0xYM2qJKcXf2yZcTG37wDKmUi6heG53vGigIavbQlLaAFvs+1mNdOu2x/0A==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.20.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-dropcursor": {
|
||||
"version": "3.20.4",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-3.20.4.tgz",
|
||||
"integrity": "sha512-TgMwvZ8myXYdmd6bUV7qkpZXv7ZUiSmX/8eo+iPEzYo2CnDLAGvDKgC50nfq/g87SDvfBgPuAiBfFvsMQQWaTw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/extensions": "^3.20.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-floating-menu": {
|
||||
"version": "3.20.4",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-3.20.4.tgz",
|
||||
"integrity": "sha512-AaPTFhoO8DBIElJyd/RTVJjkctvJuL+GHURX0npbtTxXq5HXbebVwf2ARNR7jMd/GThsmBaNJiGxZg4A2oeDqQ==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@floating-ui/dom": "^1.0.0",
|
||||
"@tiptap/core": "^3.20.4",
|
||||
"@tiptap/pm": "^3.20.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-gapcursor": {
|
||||
"version": "3.20.4",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-3.20.4.tgz",
|
||||
"integrity": "sha512-JJ6f1iQ1e0s4kISgq55U3UYGwWV/N9f0PYMtB6e3L+SBQjXnywaLK0g6vfN6IvTCC2vdIuqeSOX8VlSO97sJLw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/extensions": "^3.20.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-hard-break": {
|
||||
"version": "3.20.4",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-3.20.4.tgz",
|
||||
"integrity": "sha512-gJbq58d8zB1gzyqVEopowej5CpW4/Fpg6oGJvlZxaCukqd0gJRWGC89K+jE62YA1Td4sfcKrekKvN7jm2y/ZUg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.20.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-heading": {
|
||||
"version": "3.20.4",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-3.20.4.tgz",
|
||||
"integrity": "sha512-xsnkmTGggJc5P2iCwS1lv8KFG31xC/GNPJKoi/3UH67j/lKDhA3AdtshsLeyv2FKtTtYDb8oV0IqzHB1MM6a7w==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.20.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-horizontal-rule": {
|
||||
"version": "3.20.4",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-3.20.4.tgz",
|
||||
"integrity": "sha512-y6joCi49haAA0bo3EGUY+dWUMHH1GPUc84hxrBY/0pMs+Bn+kQ1+DQJErZDTWGJrlHPWU/yekBZT72SNdp0DNA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.20.4",
|
||||
"@tiptap/pm": "^3.20.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-italic": {
|
||||
"version": "3.20.4",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-3.20.4.tgz",
|
||||
"integrity": "sha512-4ZqiWr7cmqPFux8tj1ZLiYytyWf343IvQemNX6AvVWvscrJcrfj3YX4Le2BA0RW3A3M6RpLQXXozuF8vxYFDeQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.20.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-link": {
|
||||
"version": "3.20.4",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-3.20.4.tgz",
|
||||
"integrity": "sha512-JNDSkWrVdb8NSvbQXwHWvK5tCMbTWwOHFOweknQZ1JPK4dei9FJVofYQaHyW4bJBdcCjds3NZSnXE8DM9iAWmg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"linkifyjs": "^4.3.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.20.4",
|
||||
"@tiptap/pm": "^3.20.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-list": {
|
||||
"version": "3.20.4",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-list/-/extension-list-3.20.4.tgz",
|
||||
"integrity": "sha512-X+5plTKhOioNcQ4KsAFJJSb/3+zR8Xhdpow4HzXtoV1KcbdDey1fhZdpsfkbrzCL0s6/wAgwZuAchCK7HujurQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.20.4",
|
||||
"@tiptap/pm": "^3.20.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-list-item": {
|
||||
"version": "3.20.4",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-3.20.4.tgz",
|
||||
"integrity": "sha512-QoTc5RACXaZF+vIIBBxjGO7D0oWFUDgBKJCpvUZ0CoGGKosnfe4a9I5THFyLj4201cf0oUqgf1oZhTqETGxlVw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/extension-list": "^3.20.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-list-keymap": {
|
||||
"version": "3.20.4",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-list-keymap/-/extension-list-keymap-3.20.4.tgz",
|
||||
"integrity": "sha512-RIqXM649+8IP7p/KVfaGlJiwjCylm1m6OPlaoM3K8O7oEOGRQzNeexexECCD2jsXRxew4E+vBNMD2orXqJmu8A==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/extension-list": "^3.20.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-ordered-list": {
|
||||
"version": "3.20.4",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-3.20.4.tgz",
|
||||
"integrity": "sha512-3budNL8BgBon3TcXZ4hjT0YpFvx1Ka3uSIECKDxHgES+OQcR+6cagxSb60gFEccf3Dr0PIwcVTY6g14lC1qKRQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/extension-list": "^3.20.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-paragraph": {
|
||||
"version": "3.20.4",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-3.20.4.tgz",
|
||||
"integrity": "sha512-lm6fOScWuZAF/Sfp97igUwFd3L1QHIVLAWP5NVdh0DTLrEIt4rMBmsww+yOpMQRhvz2uTgMbMXynrimhzi/QVw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.20.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-strike": {
|
||||
"version": "3.20.4",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-3.20.4.tgz",
|
||||
"integrity": "sha512-It1Px9uDGTsVqyyg6cy7DigLoenljpQwqdI0jssM7QclZrHnsrye9fZxBBiiuCzzV1305MxKgHvratkHwqmVNA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.20.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-text": {
|
||||
"version": "3.20.4",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-3.20.4.tgz",
|
||||
"integrity": "sha512-jchJcBZixDEO2J66Zx5dchsI2mA6IYsROqF8P1poxL4ienH7RVQRCTsBNnSfIeOtREKKWeOU/tEs5fcpvvGwIQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.20.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-underline": {
|
||||
"version": "3.20.4",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-3.20.4.tgz",
|
||||
"integrity": "sha512-0OjMc3FDujX16G+jhvqcY/mLot8SrNtDu8ggUwNLAfiI/QIvMVgk7giFD71DATC/4Nb8i/iwAEegTD8MxBIXCg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.20.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extensions": {
|
||||
"version": "3.20.4",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extensions/-/extensions-3.20.4.tgz",
|
||||
"integrity": "sha512-8p6hVT65DjuQjtEdlH6ewX9SOJHlVQAOee3sWIJQmeJNRnZNvqPIBLleebUqDiljNTpxBv6s6QWkSTKgf3btwg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.20.4",
|
||||
"@tiptap/pm": "^3.20.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/pm": {
|
||||
"version": "3.20.4",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.20.4.tgz",
|
||||
"integrity": "sha512-rCHYSBToilBEuI6PtjziHDdRkABH/XqwJ7dG4Amn/SD3yGiZKYCiEApQlTUS2zZeo8DsLeuqqqB4vEOeD4OEPg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-changeset": "^2.3.0",
|
||||
"prosemirror-collab": "^1.3.1",
|
||||
"prosemirror-commands": "^1.6.2",
|
||||
"prosemirror-dropcursor": "^1.8.1",
|
||||
"prosemirror-gapcursor": "^1.3.2",
|
||||
"prosemirror-history": "^1.4.1",
|
||||
"prosemirror-inputrules": "^1.4.0",
|
||||
"prosemirror-keymap": "^1.2.2",
|
||||
"prosemirror-markdown": "^1.13.1",
|
||||
"prosemirror-menu": "^1.2.4",
|
||||
"prosemirror-model": "^1.24.1",
|
||||
"prosemirror-schema-basic": "^1.2.3",
|
||||
"prosemirror-schema-list": "^1.5.0",
|
||||
"prosemirror-state": "^1.4.3",
|
||||
"prosemirror-tables": "^1.6.4",
|
||||
"prosemirror-trailing-node": "^3.0.0",
|
||||
"prosemirror-transform": "^1.10.2",
|
||||
"prosemirror-view": "^1.38.1"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/react": {
|
||||
"version": "3.20.4",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/react/-/react-3.20.4.tgz",
|
||||
"integrity": "sha512-1B8iWsHWwb5TeyVaUs8BRPzwWo4PsLQcl03urHaz0zTJ8DauopqvxzV3+lem1OkzRHn7wnrapDvwmIGoROCaQw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/use-sync-external-store": "^0.0.6",
|
||||
"fast-equals": "^5.3.3",
|
||||
"use-sync-external-store": "^1.4.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@tiptap/extension-bubble-menu": "^3.20.4",
|
||||
"@tiptap/extension-floating-menu": "^3.20.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.20.4",
|
||||
"@tiptap/pm": "^3.20.4",
|
||||
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"@types/react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/starter-kit": {
|
||||
"version": "3.20.4",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-3.20.4.tgz",
|
||||
"integrity": "sha512-WcyK6hsTl8eBsQhQ+d9Sq8fYZKOYdL+D45MyH3hz583elXqJlW3h3JPFYb0o87gddGxn8Mm57OA/gA1zEdeDMw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tiptap/core": "^3.20.4",
|
||||
"@tiptap/extension-blockquote": "^3.20.4",
|
||||
"@tiptap/extension-bold": "^3.20.4",
|
||||
"@tiptap/extension-bullet-list": "^3.20.4",
|
||||
"@tiptap/extension-code": "^3.20.4",
|
||||
"@tiptap/extension-code-block": "^3.20.4",
|
||||
"@tiptap/extension-document": "^3.20.4",
|
||||
"@tiptap/extension-dropcursor": "^3.20.4",
|
||||
"@tiptap/extension-gapcursor": "^3.20.4",
|
||||
"@tiptap/extension-hard-break": "^3.20.4",
|
||||
"@tiptap/extension-heading": "^3.20.4",
|
||||
"@tiptap/extension-horizontal-rule": "^3.20.4",
|
||||
"@tiptap/extension-italic": "^3.20.4",
|
||||
"@tiptap/extension-link": "^3.20.4",
|
||||
"@tiptap/extension-list": "^3.20.4",
|
||||
"@tiptap/extension-list-item": "^3.20.4",
|
||||
"@tiptap/extension-list-keymap": "^3.20.4",
|
||||
"@tiptap/extension-ordered-list": "^3.20.4",
|
||||
"@tiptap/extension-paragraph": "^3.20.4",
|
||||
"@tiptap/extension-strike": "^3.20.4",
|
||||
"@tiptap/extension-text": "^3.20.4",
|
||||
"@tiptap/extension-underline": "^3.20.4",
|
||||
"@tiptap/extensions": "^3.20.4",
|
||||
"@tiptap/pm": "^3.20.4"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
}
|
||||
},
|
||||
"node_modules/@tybys/wasm-util": {
|
||||
"version": "0.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
|
||||
@@ -2204,6 +2675,22 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/linkify-it": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz",
|
||||
"integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/markdown-it": {
|
||||
"version": "14.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz",
|
||||
"integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/linkify-it": "^5",
|
||||
"@types/mdurl": "^2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/mdast": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz",
|
||||
@@ -2213,6 +2700,12 @@
|
||||
"@types/unist": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/mdurl": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz",
|
||||
"integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/ms": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
|
||||
@@ -2251,7 +2744,6 @@
|
||||
"version": "19.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.2.tgz",
|
||||
"integrity": "sha512-c1s+7TKFaDRRxr1TxccIX2u7sfCnc3RxkVyBIUA2lCpyqCF+QoAwQ/CBg7bsMdVwP120HEH143VQezKtef5nCg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "^19.0.0"
|
||||
@@ -2263,6 +2755,12 @@
|
||||
"integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/use-sync-external-store": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz",
|
||||
"integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/uuid": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz",
|
||||
@@ -3155,7 +3653,6 @@
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
||||
"dev": true,
|
||||
"license": "Python-2.0"
|
||||
},
|
||||
"node_modules/aria-query": {
|
||||
@@ -4140,6 +4637,12 @@
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/crelt": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",
|
||||
"integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||
@@ -4931,7 +5434,6 @@
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
@@ -5592,6 +6094,15 @@
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-equals": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.4.0.tgz",
|
||||
"integrity": "sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-glob": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz",
|
||||
@@ -7210,6 +7721,21 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/linkify-it": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
|
||||
"integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"uc.micro": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/linkifyjs": {
|
||||
"version": "4.3.2",
|
||||
"resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.3.2.tgz",
|
||||
"integrity": "sha512-NT1CJtq3hHIreOianA8aSXn6Cw0JzYOuDQbOrSPe7gqFnCpKP++MQe3ODgO3oh2GJFORkAAdqredOa60z63GbA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/locate-path": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
|
||||
@@ -7282,6 +7808,29 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.5.5"
|
||||
}
|
||||
},
|
||||
"node_modules/markdown-it": {
|
||||
"version": "14.1.1",
|
||||
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz",
|
||||
"integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"argparse": "^2.0.1",
|
||||
"entities": "^4.4.0",
|
||||
"linkify-it": "^5.0.0",
|
||||
"mdurl": "^2.0.0",
|
||||
"punycode.js": "^2.3.1",
|
||||
"uc.micro": "^2.1.0"
|
||||
},
|
||||
"bin": {
|
||||
"markdown-it": "bin/markdown-it.mjs"
|
||||
}
|
||||
},
|
||||
"node_modules/markdown-it-task-lists": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/markdown-it-task-lists/-/markdown-it-task-lists-2.1.1.tgz",
|
||||
"integrity": "sha512-TxFAc76Jnhb2OUu+n3yz9RMu4CwGfaT788br6HhEDlvWfdeJcLUsxk1Hgw2yJio0OXsxv7pyIPmvECY7bMbluA==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/markdown-table": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz",
|
||||
@@ -7583,6 +8132,12 @@
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/mdurl": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
|
||||
"integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/media-typer": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
|
||||
@@ -8719,6 +9274,12 @@
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/orderedmap": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-2.1.1.tgz",
|
||||
"integrity": "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/own-keys": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz",
|
||||
@@ -9288,6 +9849,201 @@
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-changeset": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-changeset/-/prosemirror-changeset-2.4.0.tgz",
|
||||
"integrity": "sha512-LvqH2v7Q2SF6yxatuPP2e8vSUKS/L+xAU7dPDC4RMyHMhZoGDfBC74mYuyYF4gLqOEG758wajtyhNnsTkuhvng==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-transform": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-collab": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-collab/-/prosemirror-collab-1.3.1.tgz",
|
||||
"integrity": "sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-state": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-commands": {
|
||||
"version": "1.7.1",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.7.1.tgz",
|
||||
"integrity": "sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.0.0",
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"prosemirror-transform": "^1.10.2"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-dropcursor": {
|
||||
"version": "1.8.2",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.8.2.tgz",
|
||||
"integrity": "sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"prosemirror-transform": "^1.1.0",
|
||||
"prosemirror-view": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-gapcursor": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-gapcursor/-/prosemirror-gapcursor-1.4.1.tgz",
|
||||
"integrity": "sha512-pMdYaEnjNMSwl11yjEGtgTmLkR08m/Vl+Jj443167p9eB3HVQKhYCc4gmHVDsLPODfZfjr/MmirsdyZziXbQKw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-keymap": "^1.0.0",
|
||||
"prosemirror-model": "^1.0.0",
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"prosemirror-view": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-history": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.5.0.tgz",
|
||||
"integrity": "sha512-zlzTiH01eKA55UAf1MEjtssJeHnGxO0j4K4Dpx+gnmX9n+SHNlDqI2oO1Kv1iPN5B1dm5fsljCfqKF9nFL6HRg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-state": "^1.2.2",
|
||||
"prosemirror-transform": "^1.0.0",
|
||||
"prosemirror-view": "^1.31.0",
|
||||
"rope-sequence": "^1.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-inputrules": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.5.1.tgz",
|
||||
"integrity": "sha512-7wj4uMjKaXWAQ1CDgxNzNtR9AlsuwzHfdFH1ygEHA2KHF2DOEaXl1CJfNPAKCg9qNEh4rum975QLaCiQPyY6Fw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"prosemirror-transform": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-keymap": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.2.3.tgz",
|
||||
"integrity": "sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"w3c-keyname": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-markdown": {
|
||||
"version": "1.13.4",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-markdown/-/prosemirror-markdown-1.13.4.tgz",
|
||||
"integrity": "sha512-D98dm4cQ3Hs6EmjK500TdAOew4Z03EV71ajEFiWra3Upr7diytJsjF4mPV2dW+eK5uNectiRj0xFxYI9NLXDbw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/markdown-it": "^14.0.0",
|
||||
"markdown-it": "^14.0.0",
|
||||
"prosemirror-model": "^1.25.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-menu": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-menu/-/prosemirror-menu-1.3.0.tgz",
|
||||
"integrity": "sha512-TImyPXCHPcDsSka2/lwJ6WjTASr4re/qWq1yoTTuLOqfXucwF6VcRa2LWCkM/EyTD1UO3CUwiH8qURJoWJRxwg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"crelt": "^1.0.0",
|
||||
"prosemirror-commands": "^1.0.0",
|
||||
"prosemirror-history": "^1.0.0",
|
||||
"prosemirror-state": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-model": {
|
||||
"version": "1.25.4",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.4.tgz",
|
||||
"integrity": "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"orderedmap": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-schema-basic": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-schema-basic/-/prosemirror-schema-basic-1.2.4.tgz",
|
||||
"integrity": "sha512-ELxP4TlX3yr2v5rM7Sb70SqStq5NvI15c0j9j/gjsrO5vaw+fnnpovCLEGIcpeGfifkuqJwl4fon6b+KdrODYQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.25.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-schema-list": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.5.1.tgz",
|
||||
"integrity": "sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.0.0",
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"prosemirror-transform": "^1.7.3"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-state": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.4.tgz",
|
||||
"integrity": "sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.0.0",
|
||||
"prosemirror-transform": "^1.0.0",
|
||||
"prosemirror-view": "^1.27.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-tables": {
|
||||
"version": "1.8.5",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-tables/-/prosemirror-tables-1.8.5.tgz",
|
||||
"integrity": "sha512-V/0cDCsHKHe/tfWkeCmthNUcEp1IVO3p6vwN8XtwE9PZQLAZJigbw3QoraAdfJPir4NKJtNvOB8oYGKRl+t0Dw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-keymap": "^1.2.3",
|
||||
"prosemirror-model": "^1.25.4",
|
||||
"prosemirror-state": "^1.4.4",
|
||||
"prosemirror-transform": "^1.10.5",
|
||||
"prosemirror-view": "^1.41.4"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-trailing-node": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-trailing-node/-/prosemirror-trailing-node-3.0.0.tgz",
|
||||
"integrity": "sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@remirror/core-constants": "3.0.0",
|
||||
"escape-string-regexp": "^4.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"prosemirror-model": "^1.22.1",
|
||||
"prosemirror-state": "^1.4.2",
|
||||
"prosemirror-view": "^1.33.8"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-transform": {
|
||||
"version": "1.11.0",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.11.0.tgz",
|
||||
"integrity": "sha512-4I7Ce4KpygXb9bkiPS3hTEk4dSHorfRw8uI0pE8IhxlK2GXsqv5tIA7JUSxtSu7u8APVOTtbUBxTmnHIxVkIJw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-view": {
|
||||
"version": "1.41.7",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.7.tgz",
|
||||
"integrity": "sha512-jUwKNCEIGiqdvhlS91/2QAg21e4dfU5bH2iwmSDQeosXJgKF7smG0YSplOWK0cjSNgIqXe7VXqo7EIfUFJdt3w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.20.0",
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"prosemirror-transform": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-addr": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||
@@ -9321,6 +10077,15 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/punycode.js": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
|
||||
"integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.14.1",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
|
||||
@@ -9746,6 +10511,12 @@
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/rope-sequence": {
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.4.tgz",
|
||||
"integrity": "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/router": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
|
||||
@@ -10809,6 +11580,46 @@
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tiptap-markdown": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/tiptap-markdown/-/tiptap-markdown-0.9.0.tgz",
|
||||
"integrity": "sha512-dKLQ9iiuGNgrlGVjrNauF/UBzWu4LYOx5pkD0jNkmQt/GOwfCJsBuzZTsf1jZ204ANHOm572mZ9PYvGh1S7tpQ==",
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
"example"
|
||||
],
|
||||
"dependencies": {
|
||||
"@types/markdown-it": "^13.0.7",
|
||||
"markdown-it": "^14.1.0",
|
||||
"markdown-it-task-lists": "^2.1.1",
|
||||
"prosemirror-markdown": "^1.11.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/tiptap-markdown/node_modules/@types/linkify-it": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.5.tgz",
|
||||
"integrity": "sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tiptap-markdown/node_modules/@types/markdown-it": {
|
||||
"version": "13.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-13.0.9.tgz",
|
||||
"integrity": "sha512-1XPwR0+MgXLWfTn9gCsZ55AHOKW1WN+P9vr0PaQh5aerR9LLQXUbjfEAFhjmEmyoYFWAyuN2Mqkn40MZ4ukjBw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/linkify-it": "^3",
|
||||
"@types/mdurl": "^1"
|
||||
}
|
||||
},
|
||||
"node_modules/tiptap-markdown/node_modules/@types/mdurl": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.5.tgz",
|
||||
"integrity": "sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
@@ -11535,6 +12346,12 @@
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/uc.micro": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
|
||||
"integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/unbox-primitive": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz",
|
||||
@@ -13028,6 +13845,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/w3c-keyname": {
|
||||
"version": "2.2.8",
|
||||
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
|
||||
"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/web-streams-polyfill": {
|
||||
"version": "4.0.0-beta.3",
|
||||
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz",
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
"@langchain/core": "^1.0.1",
|
||||
"@langchain/textsplitters": "^1.0.1",
|
||||
"@modelcontextprotocol/sdk": "^1.25.3",
|
||||
"@tiptap/react": "^3.20.4",
|
||||
"@tiptap/starter-kit": "^3.20.4",
|
||||
"@xyflow/react": "^12.10.0",
|
||||
"ai": "^6.0.27",
|
||||
"better-sqlite3": "^12.2.0",
|
||||
@@ -40,6 +42,7 @@
|
||||
"react-dom": "^19.0.1",
|
||||
"react-markdown": "^10.1.0",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"tiptap-markdown": "^0.9.0",
|
||||
"youtube-transcript": "^1.2.1",
|
||||
"youtube-transcript-plus": "^1.1.1",
|
||||
"zod": "^3.25.76"
|
||||
|
||||
+1123
-2765
File diff suppressed because it is too large
Load Diff
@@ -323,7 +323,7 @@ export default function DimensionTags({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Add dimension button - Opens modal */}
|
||||
{/* Add dimension button */}
|
||||
{!disabled && (
|
||||
<button
|
||||
onClick={(e) => {
|
||||
@@ -333,43 +333,29 @@ export default function DimensionTags({
|
||||
style={{
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
gap: '8px',
|
||||
fontSize: '11px',
|
||||
fontWeight: 600,
|
||||
color: '#22c55e',
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: '0.1em',
|
||||
borderBottom: '1px solid #1a1a1a',
|
||||
background: '#0a0a0a',
|
||||
border: 'none',
|
||||
justifyContent: 'center',
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
fontSize: '14px',
|
||||
lineHeight: 1,
|
||||
color: 'var(--rah-text-muted)',
|
||||
background: 'transparent',
|
||||
border: '1px dashed #333',
|
||||
borderRadius: '4px',
|
||||
cursor: 'pointer',
|
||||
padding: '8px 12px',
|
||||
borderRadius: '8px',
|
||||
transition: 'all 0.2s'
|
||||
transition: 'color 120ms ease, border-color 120ms ease',
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.background = '#151515';
|
||||
e.currentTarget.style.color = 'var(--rah-text-soft)';
|
||||
e.currentTarget.style.borderColor = '#555';
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.background = '#0a0a0a';
|
||||
e.currentTarget.style.color = 'var(--rah-text-muted)';
|
||||
e.currentTarget.style.borderColor = '#333';
|
||||
}}
|
||||
title="Add dimension"
|
||||
>
|
||||
<span style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: '16px',
|
||||
height: '16px',
|
||||
borderRadius: '50%',
|
||||
background: '#22c55e',
|
||||
color: '#0a0a0a',
|
||||
fontSize: '12px',
|
||||
lineHeight: 1,
|
||||
fontWeight: 300,
|
||||
flexShrink: 0
|
||||
}}>+</span>
|
||||
Add
|
||||
+
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,402 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
|
||||
interface NodeSearchModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onEdgeCreate: (nodeId: number, explanation?: string) => Promise<void>;
|
||||
excludeNodeId?: number | null;
|
||||
}
|
||||
|
||||
interface NodeSuggestion {
|
||||
id: number;
|
||||
title: string;
|
||||
dimensions?: string[];
|
||||
}
|
||||
|
||||
export default function NodeSearchModal({
|
||||
isOpen,
|
||||
onClose,
|
||||
onEdgeCreate,
|
||||
excludeNodeId,
|
||||
}: NodeSearchModalProps) {
|
||||
const [mounted, setMounted] = useState(false);
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [suggestions, setSuggestions] = useState<NodeSuggestion[]>([]);
|
||||
const [selectedIndex, setSelectedIndex] = useState(0);
|
||||
const [selectedNode, setSelectedNode] = useState<NodeSuggestion | null>(null);
|
||||
const [explanation, setExplanation] = useState('');
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
}, []);
|
||||
|
||||
const resetState = () => {
|
||||
setSearchQuery('');
|
||||
setSuggestions([]);
|
||||
setSelectedIndex(0);
|
||||
setSelectedNode(null);
|
||||
setExplanation('');
|
||||
setSubmitting(false);
|
||||
};
|
||||
|
||||
const closeModal = () => {
|
||||
resetState();
|
||||
onClose();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen) {
|
||||
resetState();
|
||||
return;
|
||||
}
|
||||
|
||||
const timer = setTimeout(() => inputRef.current?.focus(), 50);
|
||||
|
||||
const handleEscape = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Escape') {
|
||||
event.preventDefault();
|
||||
if (selectedNode) {
|
||||
setSelectedNode(null);
|
||||
setExplanation('');
|
||||
} else {
|
||||
closeModal();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('keydown', handleEscape);
|
||||
return () => {
|
||||
clearTimeout(timer);
|
||||
document.removeEventListener('keydown', handleEscape);
|
||||
};
|
||||
}, [isOpen, selectedNode]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen) return;
|
||||
if (!searchQuery.trim()) {
|
||||
setSuggestions([]);
|
||||
setSelectedIndex(0);
|
||||
return;
|
||||
}
|
||||
|
||||
const timeoutId = setTimeout(async () => {
|
||||
try {
|
||||
const response = await fetch(`/api/nodes/search?q=${encodeURIComponent(searchQuery)}&limit=10`);
|
||||
const result = await response.json();
|
||||
if (response.ok && result.success) {
|
||||
const nextSuggestions = (result.data as NodeSuggestion[])
|
||||
.filter((node) => node.id !== excludeNodeId)
|
||||
.map((node) => ({
|
||||
id: node.id,
|
||||
title: node.title,
|
||||
dimensions: node.dimensions || [],
|
||||
}));
|
||||
setSuggestions(nextSuggestions);
|
||||
setSelectedIndex(0);
|
||||
} else {
|
||||
setSuggestions([]);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching node suggestions:', error);
|
||||
setSuggestions([]);
|
||||
}
|
||||
}, 150);
|
||||
|
||||
return () => clearTimeout(timeoutId);
|
||||
}, [searchQuery, isOpen, excludeNodeId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedNode) {
|
||||
setTimeout(() => textareaRef.current?.focus(), 50);
|
||||
}
|
||||
}, [selectedNode]);
|
||||
|
||||
const handleCreate = async (node: NodeSuggestion, nextExplanation?: string) => {
|
||||
setSubmitting(true);
|
||||
try {
|
||||
await onEdgeCreate(node.id, nextExplanation?.trim() || undefined);
|
||||
closeModal();
|
||||
} catch (err) {
|
||||
console.error('Failed to create edge:', err);
|
||||
} finally {
|
||||
setSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSearchKeyDown = async (event: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (event.key === 'ArrowDown') {
|
||||
event.preventDefault();
|
||||
setSelectedIndex((prev) => Math.min(prev + 1, suggestions.length - 1));
|
||||
return;
|
||||
}
|
||||
if (event.key === 'ArrowUp') {
|
||||
event.preventDefault();
|
||||
setSelectedIndex((prev) => Math.max(prev - 1, 0));
|
||||
return;
|
||||
}
|
||||
if (event.key === 'Enter' && suggestions[selectedIndex]) {
|
||||
event.preventDefault();
|
||||
setSelectedNode(suggestions[selectedIndex]);
|
||||
setExplanation('');
|
||||
}
|
||||
};
|
||||
|
||||
if (!mounted || !isOpen) return null;
|
||||
|
||||
const modal = (
|
||||
<div
|
||||
onClick={(e) => { if (e.target === e.currentTarget) closeModal(); }}
|
||||
style={{
|
||||
position: 'fixed',
|
||||
inset: 0,
|
||||
background: 'rgba(0,0,0,0.7)',
|
||||
backdropFilter: 'blur(6px)',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
paddingTop: '15vh',
|
||||
zIndex: 9999,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
style={{
|
||||
width: '100%',
|
||||
maxWidth: '560px',
|
||||
maxHeight: '70vh',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '8px',
|
||||
alignSelf: 'flex-start',
|
||||
}}
|
||||
>
|
||||
{/* Search input */}
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '12px',
|
||||
background: '#141414',
|
||||
border: '1px solid #2a2a2a',
|
||||
borderRadius: '12px',
|
||||
padding: '14px 18px',
|
||||
boxShadow: '0 24px 48px -12px rgba(0,0,0,0.6)',
|
||||
}}>
|
||||
<svg width="16" height="16" viewBox="0 0 20 20" fill="currentColor" style={{ color: '#555', flexShrink: 0 }}>
|
||||
<path fillRule="evenodd" d="M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z" clipRule="evenodd" />
|
||||
</svg>
|
||||
<input
|
||||
ref={inputRef}
|
||||
type="text"
|
||||
value={searchQuery}
|
||||
onChange={(e) => {
|
||||
setSearchQuery(e.target.value);
|
||||
setSelectedNode(null);
|
||||
setExplanation('');
|
||||
}}
|
||||
onKeyDown={(e) => { void handleSearchKeyDown(e); }}
|
||||
placeholder="Search nodes to connect..."
|
||||
style={{
|
||||
flex: 1,
|
||||
background: 'none',
|
||||
border: 'none',
|
||||
outline: 'none',
|
||||
color: '#f0f0f0',
|
||||
fontSize: '15px',
|
||||
fontFamily: 'inherit',
|
||||
}}
|
||||
/>
|
||||
<kbd style={{
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
padding: '3px 7px',
|
||||
background: '#222',
|
||||
border: '1px solid #333',
|
||||
borderRadius: '5px',
|
||||
fontSize: '11px',
|
||||
color: '#666',
|
||||
}}>esc</kbd>
|
||||
</div>
|
||||
|
||||
{/* Results */}
|
||||
{!selectedNode && suggestions.length > 0 && (
|
||||
<div style={{
|
||||
background: '#141414',
|
||||
border: '1px solid #2a2a2a',
|
||||
borderRadius: '12px',
|
||||
overflow: 'hidden',
|
||||
boxShadow: '0 24px 48px -12px rgba(0,0,0,0.6)',
|
||||
}}>
|
||||
{suggestions.map((suggestion, index) => (
|
||||
<button
|
||||
key={suggestion.id}
|
||||
type="button"
|
||||
onClick={() => { setSelectedNode(suggestion); setExplanation(''); }}
|
||||
onMouseEnter={() => setSelectedIndex(index)}
|
||||
style={{
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '12px',
|
||||
padding: '12px 16px',
|
||||
background: index === selectedIndex ? '#1e1e1e' : 'transparent',
|
||||
border: 'none',
|
||||
borderBottom: index < suggestions.length - 1 ? '1px solid #1f1f1f' : 'none',
|
||||
cursor: 'pointer',
|
||||
textAlign: 'left',
|
||||
fontFamily: 'inherit',
|
||||
}}
|
||||
>
|
||||
<div style={{ flex: 1, minWidth: 0 }}>
|
||||
<div style={{ color: '#e0e0e0', fontSize: '13px', marginBottom: '2px' }}>
|
||||
{suggestion.title}
|
||||
</div>
|
||||
{suggestion.dimensions && suggestion.dimensions.length > 0 && (
|
||||
<div style={{
|
||||
color: '#666',
|
||||
fontSize: '11px',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
}}>
|
||||
{suggestion.dimensions.join(' · ')}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<span style={{ color: '#444', fontSize: '11px', fontFamily: 'monospace', flexShrink: 0 }}>
|
||||
#{suggestion.id}
|
||||
</span>
|
||||
{index === selectedIndex && (
|
||||
<span style={{ color: '#444', fontSize: '12px', flexShrink: 0 }}>↵</span>
|
||||
)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Selected node — explanation step */}
|
||||
{selectedNode && (
|
||||
<div style={{
|
||||
background: '#141414',
|
||||
border: '1px solid #2a2a2a',
|
||||
borderRadius: '12px',
|
||||
padding: '16px',
|
||||
boxShadow: '0 24px 48px -12px rgba(0,0,0,0.6)',
|
||||
}}>
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'flex-start',
|
||||
gap: '12px',
|
||||
marginBottom: '14px',
|
||||
}}>
|
||||
<div>
|
||||
<div style={{ color: '#555', fontSize: '10px', textTransform: 'uppercase', letterSpacing: '0.08em', marginBottom: '5px' }}>
|
||||
connecting to
|
||||
</div>
|
||||
<div style={{ color: '#e0e0e0', fontSize: '14px' }}>{selectedNode.title}</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setSelectedNode(null)}
|
||||
style={{
|
||||
border: '1px solid #333',
|
||||
background: 'transparent',
|
||||
color: '#888',
|
||||
padding: '5px 10px',
|
||||
borderRadius: '7px',
|
||||
cursor: 'pointer',
|
||||
fontSize: '11px',
|
||||
fontFamily: 'inherit',
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
Change
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div style={{ position: 'relative', marginBottom: '10px' }}>
|
||||
<textarea
|
||||
ref={textareaRef}
|
||||
value={explanation}
|
||||
onChange={(e) => setExplanation(e.target.value.slice(0, 500))}
|
||||
onKeyDown={(e) => {
|
||||
if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
void handleCreate(selectedNode, explanation);
|
||||
}
|
||||
}}
|
||||
placeholder="Describe this connection... (optional, leave blank to auto-infer)"
|
||||
rows={3}
|
||||
style={{
|
||||
width: '100%',
|
||||
padding: '10px',
|
||||
background: '#0e0e0e',
|
||||
border: '1px solid #2a2a2a',
|
||||
borderRadius: '8px',
|
||||
color: '#d0d0d0',
|
||||
fontSize: '13px',
|
||||
fontFamily: 'inherit',
|
||||
resize: 'none',
|
||||
outline: 'none',
|
||||
boxSizing: 'border-box',
|
||||
}}
|
||||
/>
|
||||
<span style={{
|
||||
position: 'absolute',
|
||||
bottom: '8px',
|
||||
right: '10px',
|
||||
fontSize: '10px',
|
||||
color: '#444',
|
||||
fontFamily: 'monospace',
|
||||
}}>
|
||||
{explanation.length}/500
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => { void handleCreate(selectedNode, explanation); }}
|
||||
disabled={submitting}
|
||||
style={{
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
padding: '10px 16px',
|
||||
background: '#1a1a1a',
|
||||
border: '1px solid #2a2a2a',
|
||||
borderRadius: '8px',
|
||||
cursor: submitting ? 'wait' : 'pointer',
|
||||
fontFamily: 'inherit',
|
||||
color: '#22c55e',
|
||||
fontSize: '13px',
|
||||
opacity: submitting ? 0.6 : 1,
|
||||
}}
|
||||
>
|
||||
{submitting ? 'Creating…' : explanation.trim() ? 'Create connection' : 'Create connection (auto-infer)'}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!searchQuery && suggestions.length === 0 && !selectedNode && (
|
||||
<div style={{
|
||||
padding: '28px 20px',
|
||||
textAlign: 'center',
|
||||
color: '#555',
|
||||
fontSize: '13px',
|
||||
}}>
|
||||
Start typing to search nodes
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return createPortal(modal, document.body);
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
"use client";
|
||||
|
||||
import { useEditor, EditorContent } from '@tiptap/react';
|
||||
import StarterKit from '@tiptap/starter-kit';
|
||||
import { Markdown } from 'tiptap-markdown';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
interface SourceEditorProps {
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
function getMarkdown(editor: ReturnType<typeof useEditor>): string {
|
||||
if (!editor) return '';
|
||||
// tiptap-markdown attaches getMarkdown to storage at runtime
|
||||
const storage = editor.storage as Record<string, any>;
|
||||
return storage?.markdown?.getMarkdown?.() ?? editor.getText();
|
||||
}
|
||||
|
||||
export default function SourceEditor({ value, onChange, disabled = false }: SourceEditorProps) {
|
||||
const editor = useEditor({
|
||||
immediatelyRender: false,
|
||||
extensions: [
|
||||
StarterKit,
|
||||
Markdown.configure({
|
||||
html: false,
|
||||
transformPastedText: true,
|
||||
transformCopiedText: true,
|
||||
}),
|
||||
],
|
||||
content: value,
|
||||
editable: !disabled,
|
||||
onUpdate({ editor }) {
|
||||
onChange(getMarkdown(editor));
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
editor?.setEditable(!disabled);
|
||||
}, [editor, disabled]);
|
||||
|
||||
// Re-sync if content changes externally (e.g. cancelled and re-opened)
|
||||
useEffect(() => {
|
||||
if (!editor || editor.isFocused) return;
|
||||
const current = getMarkdown(editor);
|
||||
if (current !== value) {
|
||||
// setContent is overridden by tiptap-markdown to parse markdown
|
||||
editor.commands.setContent(value, { emitUpdate: false });
|
||||
}
|
||||
}, [editor, value]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<EditorContent editor={editor} style={{ outline: 'none' }} />
|
||||
<style>{`
|
||||
.tiptap {
|
||||
outline: none;
|
||||
min-height: 200px;
|
||||
color: var(--rah-text-base);
|
||||
font-family: inherit;
|
||||
font-size: 15px;
|
||||
line-height: 1.7;
|
||||
caret-color: var(--rah-text-base);
|
||||
}
|
||||
|
||||
.tiptap p {
|
||||
margin: 0 0 0.85em 0;
|
||||
}
|
||||
|
||||
.tiptap p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.tiptap h1, .tiptap h2, .tiptap h3, .tiptap h4 {
|
||||
font-weight: 600;
|
||||
line-height: 1.3;
|
||||
margin: 1.1em 0 0.3em 0;
|
||||
color: var(--rah-text-active);
|
||||
}
|
||||
|
||||
.tiptap h1 { font-size: 22px; }
|
||||
.tiptap h2 { font-size: 18px; }
|
||||
.tiptap h3 { font-size: 16px; }
|
||||
.tiptap h4 { font-size: 15px; }
|
||||
|
||||
.tiptap strong { font-weight: 600; color: var(--rah-text-active); }
|
||||
.tiptap em { font-style: italic; }
|
||||
|
||||
.tiptap ul, .tiptap ol {
|
||||
padding-left: 1.3em;
|
||||
margin: 0 0 0.85em 0;
|
||||
}
|
||||
|
||||
.tiptap li { margin-bottom: 0.2em; }
|
||||
.tiptap li p { margin: 0; }
|
||||
|
||||
.tiptap blockquote {
|
||||
border-left: 2px solid var(--rah-border-strong);
|
||||
margin: 0 0 0.85em 0;
|
||||
padding-left: 1em;
|
||||
color: var(--rah-text-soft);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.tiptap code {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
|
||||
font-size: 11px;
|
||||
background: var(--rah-bg-surface);
|
||||
border: 1px solid var(--rah-border);
|
||||
border-radius: 3px;
|
||||
padding: 1px 4px;
|
||||
color: var(--rah-text-soft);
|
||||
}
|
||||
|
||||
.tiptap pre {
|
||||
background: var(--rah-bg-surface);
|
||||
border: 1px solid var(--rah-border);
|
||||
border-radius: 6px;
|
||||
padding: 12px;
|
||||
margin: 0 0 0.85em 0;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.tiptap pre code {
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tiptap hr {
|
||||
border: none;
|
||||
border-top: 1px solid var(--rah-border);
|
||||
margin: 1.4em 0;
|
||||
}
|
||||
`}</style>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useMemo, useState, useEffect, useCallback, useRef } from 'react';
|
||||
import { detectContentType, getContentTypeLabel } from './ContentDetector';
|
||||
import { detectContentType } from './ContentDetector';
|
||||
import RawFormatter from './formatters/RawFormatter';
|
||||
import TranscriptFormatter from './formatters/TranscriptFormatter';
|
||||
import BookFormatter from './formatters/BookFormatter';
|
||||
@@ -12,18 +12,14 @@ interface SourceReaderProps {
|
||||
content: string;
|
||||
onTextSelect?: (text: string) => void;
|
||||
highlightedText?: string | null;
|
||||
onContentClick?: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Source Reader - Read-only formatted view of source content
|
||||
*
|
||||
* This is a pure presentation component that NEVER modifies the source data.
|
||||
* It detects content type and applies appropriate formatting for comfortable reading.
|
||||
*/
|
||||
export default function SourceReader({
|
||||
content,
|
||||
onTextSelect,
|
||||
highlightedText,
|
||||
onContentClick,
|
||||
}: SourceReaderProps) {
|
||||
const [showSearch, setShowSearch] = useState(false);
|
||||
const [searchHighlight, setSearchHighlight] = useState<string | null>(null);
|
||||
@@ -31,14 +27,10 @@ export default function SourceReader({
|
||||
const [scrollTrigger, setScrollTrigger] = useState(0);
|
||||
const contentRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Detect content type (memoized for performance)
|
||||
const contentType = useMemo(() => detectContentType(content), [content]);
|
||||
const contentTypeLabel = getContentTypeLabel(contentType);
|
||||
|
||||
// Combined highlight: search takes precedence over external highlight
|
||||
const activeHighlight = searchHighlight || highlightedText;
|
||||
|
||||
// Keyboard shortcut for Cmd+F
|
||||
// Cmd+F to open search
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if ((e.metaKey || e.ctrlKey) && e.key === 'f') {
|
||||
@@ -46,23 +38,17 @@ export default function SourceReader({
|
||||
setShowSearch(true);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('keydown', handleKeyDown);
|
||||
return () => document.removeEventListener('keydown', handleKeyDown);
|
||||
}, []);
|
||||
|
||||
// Scroll to current match when search highlight changes
|
||||
// Scroll to current search match
|
||||
useEffect(() => {
|
||||
if (!searchHighlight || !contentRef.current) return;
|
||||
|
||||
// Small delay to let React render the mark element
|
||||
const timer = setTimeout(() => {
|
||||
const currentMatch = contentRef.current?.querySelector('mark[data-search-match="current"]');
|
||||
if (currentMatch) {
|
||||
currentMatch.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
}
|
||||
currentMatch?.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
}, 50);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, [searchHighlight, scrollTrigger]);
|
||||
|
||||
@@ -75,11 +61,9 @@ export default function SourceReader({
|
||||
const handleHighlightChange = useCallback((text: string | null, matchIndex: number) => {
|
||||
setSearchHighlight(text);
|
||||
setSearchMatchIndex(matchIndex);
|
||||
// Trigger scroll even if same text (for navigating between matches)
|
||||
setScrollTrigger(prev => prev + 1);
|
||||
}, []);
|
||||
|
||||
// Render appropriate formatter based on content type
|
||||
const renderContent = () => {
|
||||
const highlightIndex = showSearch ? searchMatchIndex : undefined;
|
||||
switch (contentType) {
|
||||
@@ -96,57 +80,8 @@ export default function SourceReader({
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
flex: 1,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
background: 'var(--rah-bg-surface)',
|
||||
borderRadius: '4px',
|
||||
overflow: 'hidden',
|
||||
}}>
|
||||
{/* Header with content type and search */}
|
||||
{content && content.length >= 50 && (
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
padding: '8px 16px',
|
||||
borderBottom: '1px solid var(--rah-border)',
|
||||
}}>
|
||||
<span style={{
|
||||
fontSize: '10px',
|
||||
color: 'var(--rah-text-muted)',
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: '0.05em',
|
||||
}}>
|
||||
Detected: {contentTypeLabel}
|
||||
</span>
|
||||
<button
|
||||
onClick={() => setShowSearch(!showSearch)}
|
||||
title="Search content (⌘F)"
|
||||
style={{
|
||||
background: showSearch ? 'var(--rah-border-strong)' : 'transparent',
|
||||
border: 'none',
|
||||
borderRadius: '4px',
|
||||
padding: '4px 8px',
|
||||
cursor: 'pointer',
|
||||
color: showSearch ? 'var(--rah-text-active)' : 'var(--rah-text-muted)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '4px',
|
||||
fontSize: '10px',
|
||||
transition: 'all 150ms ease',
|
||||
}}
|
||||
>
|
||||
<svg width="12" height="12" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fillRule="evenodd" d="M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z" clipRule="evenodd" />
|
||||
</svg>
|
||||
<span>Search</span>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Search bar */}
|
||||
<div style={{ display: 'flex', flexDirection: 'column' }}>
|
||||
{/* Search bar — only visible when active (Cmd+F) */}
|
||||
{showSearch && content && (
|
||||
<SourceSearchBar
|
||||
content={content}
|
||||
@@ -155,13 +90,16 @@ export default function SourceReader({
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Formatted content */}
|
||||
{/* Content — no chrome, no background, just the text */}
|
||||
<div
|
||||
ref={contentRef}
|
||||
style={{
|
||||
flex: 1,
|
||||
overflow: 'auto',
|
||||
onClick={() => {
|
||||
if (!onContentClick) return;
|
||||
const selected = window.getSelection?.()?.toString().trim();
|
||||
if (selected) return;
|
||||
onContentClick();
|
||||
}}
|
||||
style={{ cursor: onContentClick ? 'text' : 'default' }}
|
||||
>
|
||||
{renderContent()}
|
||||
</div>
|
||||
|
||||
@@ -124,27 +124,22 @@ export default function BookFormatter({ content, onTextSelect, highlightedText,
|
||||
ref={containerRef}
|
||||
onMouseUp={handleMouseUp}
|
||||
style={{
|
||||
maxWidth: '680px',
|
||||
margin: '0 auto',
|
||||
padding: '24px 16px',
|
||||
padding: '16px 0',
|
||||
}}
|
||||
>
|
||||
{/* Clean text display */}
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '1.5em' }}>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '1.2em' }}>
|
||||
{paragraphs.map((para, idx) => (
|
||||
<p
|
||||
key={idx}
|
||||
style={{
|
||||
fontFamily: "Georgia, 'Times New Roman', serif",
|
||||
fontSize: '16px',
|
||||
lineHeight: '1.75',
|
||||
color: '#d4d4d4',
|
||||
fontFamily: 'inherit',
|
||||
fontSize: '15px',
|
||||
lineHeight: '1.7',
|
||||
color: 'var(--rah-text-base)',
|
||||
margin: 0,
|
||||
whiteSpace: 'pre-wrap',
|
||||
wordWrap: 'break-word',
|
||||
textAlign: 'justify',
|
||||
textJustify: 'inter-word',
|
||||
hyphens: 'auto',
|
||||
}}
|
||||
>
|
||||
{renderWithHighlight(para)}
|
||||
|
||||
@@ -37,13 +37,11 @@ export default function MarkdownFormatter({ content, onTextSelect }: MarkdownFor
|
||||
ref={containerRef}
|
||||
onMouseUp={handleMouseUp}
|
||||
style={{
|
||||
maxWidth: '680px',
|
||||
margin: '0 auto',
|
||||
padding: '24px 16px',
|
||||
fontFamily: "Georgia, 'Times New Roman', serif",
|
||||
fontSize: '16px',
|
||||
lineHeight: '1.75',
|
||||
color: '#d4d4d4',
|
||||
padding: '16px 0',
|
||||
fontFamily: 'inherit',
|
||||
fontSize: '15px',
|
||||
lineHeight: '1.7',
|
||||
color: 'var(--rah-text-base)',
|
||||
}}
|
||||
>
|
||||
<MarkdownWithNodeTokens content={content} />
|
||||
|
||||
@@ -122,22 +122,20 @@ export default function RawFormatter({ content, onTextSelect, highlightedText, h
|
||||
ref={containerRef}
|
||||
onMouseUp={handleMouseUp}
|
||||
style={{
|
||||
maxWidth: '680px',
|
||||
margin: '0 auto',
|
||||
padding: '24px 16px',
|
||||
padding: '16px 0',
|
||||
}}
|
||||
>
|
||||
{paragraphs.map((paragraph, index) => (
|
||||
<p
|
||||
key={index}
|
||||
style={{
|
||||
fontFamily: "Georgia, 'Times New Roman', serif",
|
||||
fontFamily: 'inherit',
|
||||
fontSize: '15px',
|
||||
lineHeight: '1.7',
|
||||
color: '#d4d4d4',
|
||||
color: 'var(--rah-text-base)',
|
||||
margin: 0,
|
||||
marginBottom: index < paragraphs.length - 1 ? '1.5em' : 0,
|
||||
whiteSpace: 'pre-wrap', // Preserve single newlines within paragraphs
|
||||
marginBottom: index < paragraphs.length - 1 ? '1.2em' : 0,
|
||||
whiteSpace: 'pre-wrap',
|
||||
wordWrap: 'break-word',
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -174,37 +174,35 @@ export default function TranscriptFormatter({ content, onTextSelect, highlighted
|
||||
ref={containerRef}
|
||||
onMouseUp={handleMouseUp}
|
||||
style={{
|
||||
maxWidth: '680px',
|
||||
margin: '0 auto',
|
||||
padding: '24px 16px',
|
||||
padding: '16px 0',
|
||||
}}
|
||||
>
|
||||
{segments.map((segment, index) => (
|
||||
<div
|
||||
key={index}
|
||||
style={{
|
||||
marginBottom: '24px',
|
||||
marginBottom: '16px',
|
||||
}}
|
||||
>
|
||||
{/* Timestamp and speaker on same line */}
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '12px',
|
||||
marginBottom: '8px',
|
||||
gap: '8px',
|
||||
marginBottom: '4px',
|
||||
}}>
|
||||
<span style={{
|
||||
fontSize: '11px',
|
||||
fontSize: '10px',
|
||||
fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, monospace',
|
||||
color: '#555',
|
||||
color: 'var(--rah-text-muted)',
|
||||
}}>
|
||||
{segment.timestamp}
|
||||
</span>
|
||||
{segment.speaker && (
|
||||
<span style={{
|
||||
fontSize: '12px',
|
||||
fontSize: '11px',
|
||||
fontWeight: 600,
|
||||
color: '#888',
|
||||
color: 'var(--rah-text-soft)',
|
||||
}}>
|
||||
{segment.speaker}
|
||||
</span>
|
||||
@@ -213,10 +211,10 @@ export default function TranscriptFormatter({ content, onTextSelect, highlighted
|
||||
|
||||
{/* Text content */}
|
||||
<div style={{
|
||||
fontFamily: "Georgia, 'Times New Roman', serif",
|
||||
fontSize: '16px',
|
||||
lineHeight: '1.75',
|
||||
color: '#d4d4d4',
|
||||
fontFamily: 'inherit',
|
||||
fontSize: '15px',
|
||||
lineHeight: '1.7',
|
||||
color: 'var(--rah-text-base)',
|
||||
whiteSpace: 'pre-wrap',
|
||||
wordWrap: 'break-word',
|
||||
}}>
|
||||
|
||||
@@ -522,7 +522,6 @@ export class EdgeService {
|
||||
nodeId,
|
||||
nodeId,
|
||||
nodeId,
|
||||
nodeId,
|
||||
nodeId
|
||||
]);
|
||||
|
||||
|
||||
@@ -18,10 +18,17 @@ export class AutoEmbedQueue {
|
||||
private readonly cooldownMs = DEFAULT_COOLDOWN_MS;
|
||||
|
||||
async recoverStuckNodes(): Promise<void> {
|
||||
const stuckNodes = await nodeService.getNodes({ chunkStatus: 'not_chunked', limit: 1000 });
|
||||
for (const node of stuckNodes) {
|
||||
const notChunked = await nodeService.getNodes({ chunkStatus: 'not_chunked', limit: 1000 });
|
||||
for (const node of notChunked) {
|
||||
this.enqueue(node.id, { reason: 'startup_recovery' });
|
||||
}
|
||||
|
||||
// Recover nodes stuck in 'chunking' state — these were interrupted mid-process
|
||||
// and will never complete without intervention since executeTask skips them otherwise
|
||||
const stuckChunking = await nodeService.getNodes({ chunkStatus: 'chunking', limit: 1000 });
|
||||
for (const node of stuckChunking) {
|
||||
this.enqueue(node.id, { force: true, reason: 'stuck_chunking_recovery' });
|
||||
}
|
||||
}
|
||||
|
||||
enqueue(nodeId: number, task: Omit<AutoEmbedTask, 'nodeId'> = {}): boolean {
|
||||
|
||||
Reference in New Issue
Block a user