├── app ├── style.css ├── chrome │ ├── content │ │ ├── icons │ │ │ ├── icon.png │ │ │ ├── icon64.png │ │ │ ├── settings.png │ │ │ ├── loading01.gif │ │ │ ├── settings-original.png │ │ │ └── zenotes-settings-big.png │ │ └── xhtml │ │ │ ├── preferences │ │ │ ├── parts │ │ │ │ ├── contextmenu-display.xhtml │ │ │ │ ├── tesseract.xhtml │ │ │ │ ├── translation.xhtml │ │ │ │ ├── dropbox.xhtml │ │ │ │ ├── openai.xhtml │ │ │ │ ├── gemini.xhtml │ │ │ │ ├── deepseek.xhtml │ │ │ │ ├── custom-ai.xhtml │ │ │ │ └── table-display.xhtml │ │ │ ├── prefs.css │ │ │ └── prefs.xhtml │ │ │ ├── annotationeditor.xhtml │ │ │ ├── browser.xhtml │ │ │ ├── notes.css │ │ │ └── notes.xhtml │ └── core │ │ ├── migrate │ │ ├── migrate.js │ │ └── version01.js │ │ ├── engine.css │ │ └── appbase.js ├── locale │ ├── en-GB │ │ └── zenotes.ftl │ └── en-US │ │ └── zenotes.ftl ├── prefs.js ├── manifest.json └── bootstrap.js ├── src ├── Core │ ├── Migrator │ │ ├── values.json │ │ ├── One.ts │ │ └── index.ts │ ├── Cloud │ │ └── index.ts │ ├── Ocr │ │ └── index.ts │ ├── Translation │ │ ├── Translator.ts │ │ ├── index.ts │ │ ├── Format.ts │ │ ├── DeepL.ts │ │ └── Google.ts │ ├── Garbage.ts │ ├── Request.ts │ ├── Ai │ │ ├── index.ts │ │ ├── Base.ts │ │ ├── DeepSeek.ts │ │ ├── OpenAI.ts │ │ ├── Gemini.ts │ │ └── CustomAI.ts │ ├── Exporter │ │ ├── DataExporter.ts │ │ ├── Xls.ts │ │ ├── Markdown.ts │ │ ├── Xlsx.ts │ │ ├── index.ts │ │ └── PromptFormat.ts │ ├── Database.ts │ ├── ZPrefs.ts │ ├── Actions │ │ ├── TableSettings.ts │ │ └── DataSettings.ts │ ├── index.ts │ ├── Crypto.ts │ ├── Prefs.ts │ ├── Server.ts │ ├── TablePrefs.ts │ ├── Page.ts │ ├── Data.ts │ ├── Importer.ts │ └── Format.ts ├── Components │ ├── EventEmitter.ts │ ├── index.ts │ ├── NativeFieldElement.tsx │ ├── Tabs │ │ ├── TabContent.tsx │ │ ├── Tabs.module.css │ │ └── Tabs.tsx │ ├── FileElement.tsx │ ├── MenuItems │ │ ├── Icons.ts │ │ ├── NoteImageMenu.ts │ │ ├── index.ts │ │ ├── CustomAiMenu.ts │ │ ├── MenuUtils.ts │ │ ├── AnnotationImageMenu.ts │ │ ├── HeaderMenu.ts │ │ └── AnnotationQuoteMenu.ts │ ├── DataContext.ts │ ├── Dialog │ │ ├── Dialog.module.css │ │ ├── TranslationElement.tsx │ │ ├── AnnotationCommentEditor.tsx │ │ ├── DropboxDownloadDialog.tsx │ │ ├── Form.tsx │ │ ├── DataSettingDialog.tsx │ │ ├── DropboxUploadDialog.tsx │ │ ├── ExportSettingDialog.tsx │ │ └── Dialog.tsx │ ├── FindBar.module.css │ ├── ContextMenu │ │ ├── Menu.module.css │ │ └── SubMenu.tsx │ ├── Table.module.css │ ├── Color.ts │ ├── AnnotationQuoteElement.tsx │ ├── Shortcuts.tsx │ ├── Notes.tsx │ ├── AnnotationCommentElement.tsx │ ├── Cell.tsx │ ├── ColumnResizer.tsx │ ├── NoteElement.tsx │ └── FindBar.tsx ├── Config.ts ├── declarations.d.ts ├── globals.d.ts ├── globals.ts ├── Ui │ ├── index.ts │ ├── ToolBarMenu.ts │ ├── ContextMenu.ts │ └── MainMenu.ts ├── Types │ └── zty.d.ts └── index.ts ├── docs ├── images │ ├── settings-gemini.png │ ├── settings-main-01.png │ ├── settings-openai.png │ ├── screenshot-main-01.png │ ├── screenshot-main-02.png │ ├── settings-custom-ai.png │ ├── settings-deepseek.png │ ├── settings-tesseract.png │ ├── settings-custom-ai-01.png │ ├── settings-custom-ai-02.png │ ├── settings-custom-ai-03.png │ ├── settings-table-display.png │ └── settings-contextmenu-display.png ├── .gitignore ├── _config.yml └── 404.html ├── commands ├── esbuild.inject.js ├── build.mjs ├── test.mjs ├── postbuild.mjs ├── dev.mjs ├── analyze.mjs └── settings.mjs ├── .gitignore ├── tsconfig.json ├── .github └── ISSUE_TEMPLATE │ ├── question.md │ ├── feature_request.md │ └── bug_report.md ├── zenotes-update.json ├── package.json └── README.md /app/style.css: -------------------------------------------------------------------------------- 1 | [aria-label*="[Ai column notes]"] { 2 | color: #e63946; 3 | } 4 | -------------------------------------------------------------------------------- /src/Core/Migrator/values.json: -------------------------------------------------------------------------------- 1 | { 2 | "target": "1", 3 | "source": "0", 4 | "mode":"" 5 | } -------------------------------------------------------------------------------- /src/Core/Migrator/One.ts: -------------------------------------------------------------------------------- 1 | 2 | const One = { 3 | Crypto: { 4 | 5 | } 6 | } 7 | 8 | export default One; -------------------------------------------------------------------------------- /docs/images/settings-gemini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frianasoa/Ze-Notes/HEAD/docs/images/settings-gemini.png -------------------------------------------------------------------------------- /docs/images/settings-main-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frianasoa/Ze-Notes/HEAD/docs/images/settings-main-01.png -------------------------------------------------------------------------------- /docs/images/settings-openai.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frianasoa/Ze-Notes/HEAD/docs/images/settings-openai.png -------------------------------------------------------------------------------- /src/Components/EventEmitter.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from 'events'; 2 | export const emitter = new EventEmitter(); -------------------------------------------------------------------------------- /app/chrome/content/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frianasoa/Ze-Notes/HEAD/app/chrome/content/icons/icon.png -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | _site 2 | .sass-cache 3 | .jekyll-cache 4 | .jekyll-metadata 5 | vendor 6 | Gemfile.lock 7 | Gemfile 8 | -------------------------------------------------------------------------------- /docs/images/screenshot-main-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frianasoa/Ze-Notes/HEAD/docs/images/screenshot-main-01.png -------------------------------------------------------------------------------- /docs/images/screenshot-main-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frianasoa/Ze-Notes/HEAD/docs/images/screenshot-main-02.png -------------------------------------------------------------------------------- /docs/images/settings-custom-ai.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frianasoa/Ze-Notes/HEAD/docs/images/settings-custom-ai.png -------------------------------------------------------------------------------- /docs/images/settings-deepseek.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frianasoa/Ze-Notes/HEAD/docs/images/settings-deepseek.png -------------------------------------------------------------------------------- /docs/images/settings-tesseract.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frianasoa/Ze-Notes/HEAD/docs/images/settings-tesseract.png -------------------------------------------------------------------------------- /app/chrome/content/icons/icon64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frianasoa/Ze-Notes/HEAD/app/chrome/content/icons/icon64.png -------------------------------------------------------------------------------- /app/chrome/content/icons/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frianasoa/Ze-Notes/HEAD/app/chrome/content/icons/settings.png -------------------------------------------------------------------------------- /docs/images/settings-custom-ai-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frianasoa/Ze-Notes/HEAD/docs/images/settings-custom-ai-01.png -------------------------------------------------------------------------------- /docs/images/settings-custom-ai-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frianasoa/Ze-Notes/HEAD/docs/images/settings-custom-ai-02.png -------------------------------------------------------------------------------- /docs/images/settings-custom-ai-03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frianasoa/Ze-Notes/HEAD/docs/images/settings-custom-ai-03.png -------------------------------------------------------------------------------- /app/chrome/content/icons/loading01.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frianasoa/Ze-Notes/HEAD/app/chrome/content/icons/loading01.gif -------------------------------------------------------------------------------- /docs/images/settings-table-display.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frianasoa/Ze-Notes/HEAD/docs/images/settings-table-display.png -------------------------------------------------------------------------------- /src/Config.ts: -------------------------------------------------------------------------------- 1 | import packageJson from '../package.json'; 2 | 3 | const Config = { ...packageJson.config }; 4 | export default Config; -------------------------------------------------------------------------------- /app/locale/en-GB/zenotes.ftl: -------------------------------------------------------------------------------- 1 | zenotes-deepl-translate = 2 | .label = DeepL translate 3 | zenotes-attachment = 4 | .label = Attachment -------------------------------------------------------------------------------- /app/locale/en-US/zenotes.ftl: -------------------------------------------------------------------------------- 1 | zenotes-deepl-translate = 2 | .label = DeepL translate 3 | zenotes-attachment = 4 | .label = Attachment -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | remote_theme: pages-themes/dinky@v0.2.0 2 | plugins: 3 | - jekyll-remote-theme 4 | kramdown: 5 | hard_wrap: true 6 | -------------------------------------------------------------------------------- /src/declarations.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.module.css' { 2 | const classes: { [key: string]: string }; 3 | export default classes; 4 | } -------------------------------------------------------------------------------- /commands/esbuild.inject.js: -------------------------------------------------------------------------------- 1 | import { Buffer } from "buffer"; 2 | export let process = require("process/browser") 3 | globalThis.Buffer = Buffer; -------------------------------------------------------------------------------- /docs/images/settings-contextmenu-display.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frianasoa/Ze-Notes/HEAD/docs/images/settings-contextmenu-display.png -------------------------------------------------------------------------------- /app/chrome/content/icons/settings-original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frianasoa/Ze-Notes/HEAD/app/chrome/content/icons/settings-original.png -------------------------------------------------------------------------------- /app/chrome/content/icons/zenotes-settings-big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frianasoa/Ze-Notes/HEAD/app/chrome/content/icons/zenotes-settings-big.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | TODO.md 2 | run.cmd 3 | run7.cmd 4 | stats.py 5 | make.py 6 | beta.py 7 | docs.sh 8 | readme.sh 9 | upload.sh 10 | merge.sh 11 | node_modules/ 12 | zn@alefa.net 13 | build/ -------------------------------------------------------------------------------- /commands/build.mjs: -------------------------------------------------------------------------------- 1 | import * as esbuild from 'esbuild'; 2 | import { createBuildSettings } from './settings.mjs'; 3 | 4 | const settings = createBuildSettings({ minify: true }); 5 | await esbuild.build(settings); -------------------------------------------------------------------------------- /commands/test.mjs: -------------------------------------------------------------------------------- 1 | import * as esbuild from 'esbuild'; 2 | import { createBuildSettings } from './settings.mjs'; 3 | 4 | const settings = createBuildSettings({ minify: false }); 5 | await esbuild.build(settings); -------------------------------------------------------------------------------- /src/Core/Cloud/index.ts: -------------------------------------------------------------------------------- 1 | import Dropbox from './Dropbox'; 2 | 3 | type CloudType = { 4 | Dropbox: typeof Dropbox; 5 | }; 6 | 7 | const Cloud: CloudType = { 8 | Dropbox 9 | }; 10 | 11 | export default Cloud; -------------------------------------------------------------------------------- /src/Core/Ocr/index.ts: -------------------------------------------------------------------------------- 1 | import Tesseract from './Tesseract'; 2 | 3 | type OcrType = { 4 | Tesseract: typeof Tesseract; 5 | }; 6 | 7 | const Ocr: OcrType = { 8 | Tesseract 9 | }; 10 | 11 | export default Ocr; -------------------------------------------------------------------------------- /src/globals.d.ts: -------------------------------------------------------------------------------- 1 | declare global { 2 | var Zotero: _ZoteroTypes.Zotero; 3 | var Zotero_File_Exporter: any; 4 | var Zotero_Tabs: _ZoteroTypes.Zotero_Tabs; 5 | } 6 | 7 | declare module '*.module.css' 8 | 9 | export {}; 10 | -------------------------------------------------------------------------------- /src/Core/Translation/Translator.ts: -------------------------------------------------------------------------------- 1 | export interface Translator { 2 | translate(sentence: string, language: string): Promise; 3 | translatewithkey?: (sentence: string, language: string, apikey:string|null) => Promise; 4 | keyisvalid?:(apikey: string) => Promise; 5 | } -------------------------------------------------------------------------------- /commands/postbuild.mjs: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | 3 | function copycss() { 4 | const source = "./app/chrome/core/engine.css"; 5 | const dest = "./app/chrome/content/xhtml/notes.css"; 6 | const data = fs.readFileSync(source); 7 | fs.writeFileSync(dest, data); 8 | } 9 | 10 | copycss(); -------------------------------------------------------------------------------- /commands/dev.mjs: -------------------------------------------------------------------------------- 1 | import * as esbuild from 'esbuild'; 2 | import { createBuildSettings } from './settings.mjs'; 3 | import { typecheckPlugin } from '@jgoz/esbuild-plugin-typecheck'; 4 | 5 | const settings = createBuildSettings({ minify: false}); 6 | settings.plugins.push(typecheckPlugin()) 7 | await esbuild.build(settings); -------------------------------------------------------------------------------- /app/prefs.js: -------------------------------------------------------------------------------- 1 | pref("extensions.zenotes.v1.intensity", 100); 2 | pref("extensions.zenotes.v1.main-comment-label", "Main comment"); 3 | pref("extensions.zenotes.v1.untagged-column-label", "Untagged"); 4 | pref("extensions.zenotes.v1.default-columns", "[\"source\", \"journal\", \"title\"]"); 5 | pref("extensions.zenotes.v1.allow-duplicate-rows", "false"); -------------------------------------------------------------------------------- /src/Components/index.ts: -------------------------------------------------------------------------------- 1 | import Notes from './Notes'; 2 | import FindBar from './FindBar'; 3 | import {emitter} from './EventEmitter'; 4 | 5 | type ComponentsType = { 6 | Notes: typeof Notes, 7 | FindBar: typeof FindBar, 8 | emitter: typeof emitter, 9 | }; 10 | 11 | const Components: ComponentsType = { 12 | Notes, 13 | emitter, 14 | FindBar 15 | }; 16 | 17 | export default Components; -------------------------------------------------------------------------------- /src/Core/Translation/index.ts: -------------------------------------------------------------------------------- 1 | import Google from './Google'; 2 | import Languages from './Languages'; 3 | import DeepL from './DeepL'; 4 | 5 | type TranslationType = { 6 | Languages: typeof Languages; 7 | Google: typeof Google; 8 | DeepL: typeof DeepL; 9 | }; 10 | 11 | const Translation: TranslationType = { 12 | Google, 13 | DeepL, 14 | Languages 15 | }; 16 | 17 | export default Translation; 18 | -------------------------------------------------------------------------------- /app/chrome/content/xhtml/preferences/parts/contextmenu-display.xhtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 |
Font size 6 | 7 |
10 |
-------------------------------------------------------------------------------- /src/globals.ts: -------------------------------------------------------------------------------- 1 | function createXULElement(document: Document, tag: string): Element { 2 | const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; 3 | return document.createElementNS(XUL_NS, tag); 4 | } 5 | 6 | const Zotero_Tabs = Zotero.getMainWindow().Zotero_Tabs; 7 | globalThis.console = Zotero.getMainWindow().console; 8 | 9 | export { 10 | createXULElement, 11 | Zotero_Tabs 12 | } -------------------------------------------------------------------------------- /src/Components/NativeFieldElement.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | type NativeFieldElementProps = { 4 | item: Record 5 | }; 6 | 7 | const NativeFieldElement: React.FC = ({ item }) => { 8 | return ( 9 |
10 | {item.text} 11 |
12 | ); 13 | }; 14 | 15 | export default NativeFieldElement; 16 | -------------------------------------------------------------------------------- /src/Components/Tabs/TabContent.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styles from './Tabs.module.css'; 3 | 4 | const TabContent: React.FC<{ activeTab: number; tabId: number; content: React.ComponentType }> = ({ 5 | activeTab, 6 | tabId, 7 | content: Content, 8 | }) => { 9 | return activeTab === tabId ? ( 10 |
11 | 12 |
13 | ) : null; 14 | }; 15 | 16 | export default TabContent; -------------------------------------------------------------------------------- /docs/404.html: -------------------------------------------------------------------------------- 1 | 14 | 15 |
16 |

404

17 | 18 |

Page not found :(

19 |

The requested page could not be found.

20 |
21 | -------------------------------------------------------------------------------- /src/Core/Garbage.ts: -------------------------------------------------------------------------------- 1 | const Garbage = { 2 | addedElementIDs: [] as string[], 3 | collect(item: Element) { 4 | if (!item.id) { 5 | throw "Element must have an id"; 6 | } 7 | this.addedElementIDs.push(item.id); 8 | }, 9 | 10 | freeall(doc: Document) { 11 | for (const id of this.addedElementIDs) { 12 | const elem = doc?.getElementById(id); 13 | if (elem) elem.remove(); 14 | } 15 | } 16 | }; 17 | 18 | export default Garbage; 19 | -------------------------------------------------------------------------------- /src/Ui/index.ts: -------------------------------------------------------------------------------- 1 | import ContextMenu from './ContextMenu'; 2 | import MainMenu from './MainMenu'; 3 | 4 | type UiType = { 5 | ContextMenu: typeof ContextMenu; 6 | MainMenu: typeof MainMenu; 7 | init(config: {rootURI: string}): void; 8 | }; 9 | 10 | const Ui: UiType = { 11 | ContextMenu, 12 | MainMenu, 13 | init({rootURI}:{rootURI: string}) 14 | { 15 | ContextMenu.init({rootURI}); 16 | MainMenu.init({rootURI}); 17 | } 18 | }; 19 | 20 | export default Ui; -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "sourceMap": true, 5 | "resolveJsonModule": true, 6 | "esModuleInterop": true, 7 | "target": "es2015", 8 | "module": "ES2015", 9 | "jsx": "react-jsx", 10 | "moduleResolution": "node", 11 | "skipLibCheck": true, 12 | "lib": [ 13 | "ES2023", 14 | "DOM" 15 | ], 16 | "allowSyntheticDefaultImports": true 17 | }, 18 | "include": ["src", "node_modules/zotero-types"] 19 | } -------------------------------------------------------------------------------- /src/Components/FileElement.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | type FileElementProps = { 4 | item: Record; 5 | }; 6 | 7 | const FileElement: React.FC = ({item}) => { 8 | return ( 9 |
10 | Attachments 11 |
12 | {item.key} 13 |
14 |
15 | ); 16 | }; 17 | 18 | export default FileElement; 19 | -------------------------------------------------------------------------------- /src/Core/Request.ts: -------------------------------------------------------------------------------- 1 | const Request = { 2 | send(url: string, options: any, format:any=(data:any)=>data) 3 | { 4 | return fetch(url, options). 5 | then(async res => { 6 | if (!res.ok) { 7 | throw await res.json() 8 | } 9 | return res.json() 10 | }) 11 | .then(data => format(data)). 12 | catch(async error =>{ 13 | throw error.error?.message || error.error || error.message || JSON.stringify(error); 14 | }) 15 | } 16 | } 17 | 18 | export default Request; -------------------------------------------------------------------------------- /src/Core/Ai/index.ts: -------------------------------------------------------------------------------- 1 | import OpenAI from './OpenAI'; 2 | import Gemini from './Gemini'; 3 | import DeepSeek from './DeepSeek'; 4 | import CustomAI from './CustomAI'; 5 | import AiNotes from './AiNotes'; 6 | 7 | type AiType = { 8 | OpenAI: typeof OpenAI; 9 | Gemini: typeof Gemini; 10 | DeepSeek: typeof DeepSeek; 11 | CustomAI: typeof CustomAI; 12 | AiNotes: typeof AiNotes; 13 | }; 14 | 15 | const Ai: AiType = { 16 | AiNotes, 17 | CustomAI, 18 | OpenAI, 19 | Gemini, 20 | DeepSeek 21 | }; 22 | 23 | export default Ai; -------------------------------------------------------------------------------- /src/Core/Exporter/DataExporter.ts: -------------------------------------------------------------------------------- 1 | import FileExporter from "./FileExporter"; 2 | const DataExporter = { 3 | async exportall(collectionid: number, email: string) 4 | { 5 | const collection = Zotero.Collections.get(collectionid) 6 | if(!collection) 7 | { 8 | throw "You can only export a collection!"; 9 | } 10 | if(collection.getChildItems().length<=0) 11 | { 12 | throw "You cannot export an empty collection!"; 13 | } 14 | return FileExporter.autosave(collection, email); 15 | }, 16 | } 17 | 18 | export default DataExporter; -------------------------------------------------------------------------------- /commands/analyze.mjs: -------------------------------------------------------------------------------- 1 | import * as esbuild from 'esbuild'; 2 | import fs from 'node:fs'; 3 | import { createBuildSettings } from './settings.mjs'; 4 | 5 | const settings = createBuildSettings({ minify: true, metafile: true }); 6 | const result = await esbuild.build(settings); 7 | const mode = process.env.npm_config_mode; 8 | 9 | if (mode === "write") { 10 | fs.writeFileSync("build-meta.json", JSON.stringify(result.metafile)) 11 | } else { 12 | console.log(await esbuild.analyzeMetafile(result.metafile, { 13 | verbose: false, 14 | })); 15 | } -------------------------------------------------------------------------------- /app/chrome/content/xhtml/preferences/parts/tesseract.xhtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
Language
Executable path
12 |
-------------------------------------------------------------------------------- /src/Components/MenuItems/Icons.ts: -------------------------------------------------------------------------------- 1 | const Icons = { 2 | data: { 3 | "journal-article": "chrome://zotero/skin/item-type/16/light/journal-article.svg", 4 | "main": '/content/icons/icon.png', 5 | "edit": 'chrome://zotero/skin/16/universal/edit.svg', 6 | "note": "chrome://zotero/skin/item-type/16/light/note.svg", 7 | "annotation": "chrome://zotero/skin/itempane/16/attachment-annotations.svg", 8 | "note-delete": "chrome://zotero/skin/item-type/16/light/annotate-eraser.svg", 9 | "settings": "/content/icons/settings.png" 10 | } 11 | } 12 | 13 | export default Icons; -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question or Help 3 | about: Ask a question or request guidance about using the plugin 4 | title: "[QUESTION] " 5 | labels: question 6 | assignees: '' 7 | 8 | --- 9 | 10 | **What do you need help with?** 11 | Describe your question as clearly as possible. 12 | 13 | **What have you tried so far?** 14 | Let us know what you've read or tried (e.g. documentation, existing issues). 15 | 16 | **Zotero and Plugin Details** 17 | - Zotero Version: 18 | - Plugin Version: 19 | - Operating System: 20 | 21 | **Additional context** 22 | Links, logs, or anything else that might help. 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: Suggest a new feature or improvement for the plugin 4 | title: "[FEATURE] " 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem?** 11 | Explain the issue that this feature would help solve. 12 | 13 | **Describe the solution you'd like** 14 | Be specific about the behavior or UI you're proposing. 15 | 16 | **Describe alternatives you've considered** 17 | If you tried a workaround or another plugin, let us know. 18 | 19 | **Additional context** 20 | Add any mockups, related links, or notes here. 21 | -------------------------------------------------------------------------------- /app/chrome/content/xhtml/preferences/parts/translation.xhtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
Language
DeepL API key
Google translate API key
16 |
-------------------------------------------------------------------------------- /zenotes-update.json: -------------------------------------------------------------------------------- 1 | { 2 | "addons": { 3 | "zenotes@alefa.net": { 4 | "updates": [ 5 | { 6 | "version": "1.0.5", 7 | "update_link": "https://github.com/frianasoa/Ze-Notes/releases/download/v1.0.5/zenotes-v1.0.5.xpi", 8 | "applications": { 9 | "zotero": { 10 | "strict_min_version": "7.0.0-beta.70", 11 | "strict_max_version": "8.1.*" 12 | } 13 | }, 14 | "update_hash": "sha512:8f3b822f4e8d00afca7f460d411d5c4c5c15dd0070af8cf9af41f6669eaada5f07b75b5df899fa720a731b8239bfa0052e8db9f94fb1147d432161e4a374e565" 15 | } 16 | ] 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/Components/DataContext.ts: -------------------------------------------------------------------------------- 1 | import React, { createContext } from 'react'; 2 | 3 | interface DataContextType { 4 | collectionid?: string; 5 | collectionname?: string; 6 | MenuItems?: any; 7 | initColumnWidths?: () => void; 8 | isTranslationDialogOpen?: boolean; 9 | setIsTranslationDialogOpen?: (isOpen: boolean) => void; 10 | translationDialogState?: any; 11 | setTranslationDialogState?: (value: any) => void; 12 | commonDialogState?: any; 13 | setCommonDialogState?: (value: any) => void; 14 | setLoadingMessage?: (value: string) => void; 15 | setIsLoading?: (value: boolean) => void; 16 | } 17 | 18 | const DataContext = createContext(null); 19 | 20 | export default DataContext; -------------------------------------------------------------------------------- /commands/settings.mjs: -------------------------------------------------------------------------------- 1 | // import { sassPlugin } from 'esbuild-sass-plugin'; 2 | // import CssModulesPlugin from 'esbuild-css-modules-plugin'; 3 | 4 | export function createBuildSettings(options) { 5 | return { 6 | entryPoints: ['src/index.ts'], 7 | outfile: 'app/chrome/core/engine.js', 8 | bundle: true, 9 | target: "firefox115", 10 | globalName: 'Engine', 11 | platform: 'browser', 12 | format: "iife", 13 | inject: [ 14 | "commands/esbuild.inject.js" 15 | ], 16 | define: { 17 | 'process.env.NODE_ENV': '"development"', 18 | 'global': 'globalThis' 19 | }, 20 | plugins: [], 21 | // plugins: [CssModulesPlugin(), sassPlugin({type: "style"})], 22 | ...options 23 | }; 24 | } -------------------------------------------------------------------------------- /app/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "Ze Notes", 4 | "version": "1.0.5", 5 | "description": "Advanced notes manager", 6 | "author": "Fanantenana Rianasoa Andriariniaina", 7 | "homepage_url": "https://github.com/frianasoa/zenotes", 8 | "icons": { 9 | "48": "chrome/content/icons/icon.png", 10 | "64": "chrome/content/icons/icon64.png" 11 | }, 12 | "applications": { 13 | "zotero": { 14 | "id": "zenotes@alefa.net", 15 | "update_url": "https://github.com/frianasoa/zenotes/releases/download/release/zenotes-update.json", 16 | "strict_min_version": "7.0.0-beta.70", 17 | "strict_max_version": "8.1.*" 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/Core/Translation/Format.ts: -------------------------------------------------------------------------------- 1 | 2 | const Format = { 3 | google(data: any) { 4 | try { 5 | return [data[0].map((e:any)=>{return e[0]}).join(" ")]; 6 | } 7 | catch(e) 8 | { 9 | try { 10 | return [data.data.translations.map((e:any)=>{return e.translatedText}).join(" ")]; 11 | } 12 | catch(e1) 13 | { 14 | return [e1]; 15 | } 16 | return [e]; 17 | } 18 | }, 19 | 20 | deepl(data:any) 21 | { 22 | try { 23 | return Promise.resolve(data.translations.map(function(e: any){return e.text})); 24 | } 25 | catch(e) 26 | { 27 | return Promise.resolve(["Error: "+e, JSON.stringify(data)]); 28 | } 29 | } 30 | } 31 | 32 | export default Format; -------------------------------------------------------------------------------- /src/Components/Dialog/Dialog.module.css: -------------------------------------------------------------------------------- 1 | @media (prefers-color-scheme: light) { 2 | .header { 3 | background-color: #f5f5f5; 4 | } 5 | .header button { 6 | 7 | } 8 | .body { 9 | 10 | } 11 | .body fieldset { 12 | 13 | } 14 | .overlay { 15 | background-color: rgba(0, 0, 0, 0.5); 16 | } 17 | } 18 | 19 | @media (prefers-color-scheme: dark) { 20 | .header { 21 | background-color: #011036; 22 | border-bottom: solid 1px white; 23 | color: white; 24 | } 25 | 26 | .header button { 27 | color: white; 28 | } 29 | 30 | .body { 31 | background-color: #121212; 32 | color: white; 33 | } 34 | 35 | .body fieldset { 36 | border: solid 1px white; 37 | } 38 | 39 | .overlay { 40 | background-color: rgba(255, 255, 255, 0.5); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Core/Exporter/Xls.ts: -------------------------------------------------------------------------------- 1 | const Xls = { 2 | start(html: string) 3 | { 4 | const template = '{html}
'; 5 | const format = (s: any, c: any)=> { 6 | return s.replace(/{(\w+)}/g, function(m: any, p: any) { return c[p]; }); 7 | } 8 | const ctx = {worksheet:"data", html}; 9 | return format(template, ctx); 10 | } 11 | } 12 | 13 | export default Xls -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Create a report to help us improve 4 | title: "[BUG] " 5 | labels: bug 6 | assignees: '' 7 | --- 8 | 9 | **Describe the bug** 10 | A clear and concise description of what the bug is. 11 | 12 | **To Reproduce** 13 | Steps to reproduce the behavior: 14 | 1. Go to '...' 15 | 2. Click on '....' 16 | 3. Scroll down to '....' 17 | 4. See error 18 | 19 | **Expected behavior** 20 | A clear and concise description of what you expected to happen. 21 | 22 | **Screenshots** 23 | If applicable, add screenshots to help explain your problem. 24 | 25 | **Environment (please complete the following information):** 26 | - OS: [e.g. Windows, macOS, Linux] 27 | - Zotero Version [e.g. 7.1.0] 28 | - Plugin Version [e.g. 1.0.0] 29 | 30 | **Additional context** 31 | Add any other context about the problem here. -------------------------------------------------------------------------------- /app/chrome/content/xhtml/preferences/prefs.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | hr { 6 | width: 100%; 7 | border: none; 8 | background-color: lightgray; 9 | height: 1px; 10 | margin-top: 1em; 11 | } 12 | 13 | .zenotes-values { 14 | font-size: 1em; 15 | padding: 0.4em; 16 | margin: 0.1em; 17 | border: 1px solid #ccc; 18 | border-radius: 0.3em; 19 | text-align: left; 20 | min-width: 35em; 21 | width: 100%; 22 | } 23 | 24 | textarea.zenotes-values { 25 | min-height: 6em; 26 | height: auto; 27 | } 28 | 29 | textarea.resizeable { 30 | resize: vertical; 31 | } 32 | 33 | .setting-part { 34 | width: 95%; 35 | } 36 | 37 | table { 38 | width: 100%; 39 | } 40 | 41 | .zn-settings1 td { 42 | border-bottom: solid 1px white; 43 | } 44 | 45 | /* td { */ 46 | /* border: solid 1px red; */ 47 | /* } */ 48 | 49 | /* tr td:nth-child(2) * { */ 50 | /* width: 100%; */ 51 | /* } */ -------------------------------------------------------------------------------- /src/Core/Database.ts: -------------------------------------------------------------------------------- 1 | import pkg from "../../package.json"; 2 | 3 | const Database = { 4 | OLDDB: Zotero.DBConnection, 5 | DB: Zotero.DBConnection, 6 | version: "v1", 7 | async open() 8 | { 9 | Database.OLDDB = new Zotero.DBConnection("zenotes"); 10 | Database.DB = new Zotero.DBConnection(pkg.config.slug+"-"+Database.version); 11 | await Database.create(); 12 | }, 13 | 14 | async close(permanent=false) 15 | { 16 | await Database.OLDDB.closeDatabase(permanent); 17 | await Database.DB.closeDatabase(permanent); 18 | }, 19 | 20 | async create() 21 | { 22 | var q1 = "CREATE TABLE IF NOT EXISTS `preferences` (id INTEGER PRIMARY KEY, `key` TEXT UNIQUE, `value` TEXT)"; 23 | var q2 = "CREATE TABLE IF NOT EXISTS `tableoptions` (id INTEGER PRIMARY KEY, collectionid TEXT, key TEXT, value TEXT, UNIQUE(collectionid, key))"; 24 | await Database.DB.queryAsync(q1, []); 25 | await Database.DB.queryAsync(q2, []); 26 | }, 27 | } 28 | 29 | export default Database -------------------------------------------------------------------------------- /src/Types/zty.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace zty { 2 | type Row = { 3 | id: any; 4 | itemid: any; 5 | key: any; 6 | title: any; 7 | date: any; 8 | journal: any; 9 | source: any; 10 | zpaths: string; 11 | [key: string]: any; 12 | }; 13 | 14 | type ContextMenuData = { 15 | state?: string; 16 | label?: string; 17 | icon?: any; 18 | title?: string; 19 | iconColor?: string; 20 | textColor?: string; 21 | bgColor?: string; 22 | data?: any; 23 | type?: string; 24 | options?: any; 25 | onClick?:(item: ContextMenuData, celldata: Record, event?: any) => void; 26 | onClose?:() => void; 27 | submenu?: Record; 28 | }; 29 | 30 | type ItemData = { 31 | type?: string; 32 | key?: string; 33 | value?: string; 34 | text?: string; 35 | comment?: string; 36 | pagelabel?: string; 37 | image?: string; 38 | }; 39 | 40 | } 41 | 42 | type Position = "absolute" | "relative" | "fixed" | "sticky" | "static"; 43 | -------------------------------------------------------------------------------- /src/Components/FindBar.module.css: -------------------------------------------------------------------------------- 1 | .findbar { 2 | background: transparent; 3 | display: flex; 4 | flex-direction: row; 5 | gap: 0.8em; 6 | width: 25em; 7 | padding: 0.2em; 8 | } 9 | 10 | .findinput { 11 | display: inline-block; 12 | margin: 0; 13 | padding: 0.3em; 14 | height: 1.6em; 15 | flex: 1; 16 | } 17 | 18 | .findbutton { 19 | display: inline-block; 20 | border: none; 21 | border-radius: 0.2em; 22 | background: transparent; 23 | margin: 0; 24 | padding: 0.1em; 25 | width: 2.6em; 26 | cursor: pointer; 27 | transition: background-color 0.3s ease; 28 | height: 100%; 29 | } 30 | 31 | .findbutton:hover { 32 | background-color: rgba(100, 100, 100, 0.1); /* Light background on hover */ 33 | } 34 | 35 | .icon { 36 | padding: 0; 37 | margin: 0; 38 | size: 1.3em; 39 | } 40 | 41 | 42 | 43 | .findnext { 44 | /* Optional specific styles for the "Find Next" button */ 45 | } 46 | 47 | .findprevious { 48 | /* Optional specific styles for the "Find Previous" button */ 49 | } -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import Ui from './Ui'; 2 | import Config from './Config'; 3 | import Core from './Core'; 4 | import Components from './Components'; 5 | import ReaderMenu from './Components/ContextMenu/ReaderMenu'; 6 | 7 | type EngineType = { 8 | Config: typeof Config; 9 | Ui: typeof Ui; 10 | Core: typeof Core; 11 | Components: typeof Components; 12 | ReaderMenu: typeof ReaderMenu; 13 | id: string | null; 14 | version: string | null; 15 | rootURI: string | null; 16 | init(config: { id: string; version: string; rootURI: string}): void; 17 | }; 18 | 19 | const Engine: EngineType = { 20 | Config, 21 | Ui, 22 | Core, 23 | Components, 24 | ReaderMenu, 25 | id: null, 26 | version: null, 27 | rootURI: null, 28 | init({ id, version, rootURI}: { id: string; version: string; rootURI: string}) 29 | { 30 | this.id = id; 31 | this.version = version; 32 | this.rootURI = rootURI; 33 | this.Ui.init({rootURI}); 34 | this.Core.Server.init(); 35 | } 36 | }; 37 | 38 | export { Engine }; 39 | -------------------------------------------------------------------------------- /app/chrome/content/xhtml/preferences/prefs.xhtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Loading... 14 |
Loading ...
15 |
-------------------------------------------------------------------------------- /src/Components/MenuItems/NoteImageMenu.ts: -------------------------------------------------------------------------------- 1 | import Actions from '../../Core/Actions'; 2 | import Tesseract from "../../Core/Ocr/Tesseract"; 3 | import {FaT} from "react-icons/fa6"; 4 | 5 | const NoteImageMenu = { 6 | show(event: Event, context: any, item: Record) { 7 | let lang = Zotero.Prefs.get('extensions.zenotes.tesseract-language', true); 8 | if(!lang){lang = "en"}else{lang = String(lang)} 9 | const language = Tesseract.langname(lang); 10 | 11 | context.MenuItems.main["ocrnoteimage"] = { 12 | label: 'OCR note image', 13 | title: "language used: "+language, 14 | icon: FaT, 15 | onClick: Actions.ocrnote, 16 | data: {itemkey: item.itemkey, collectionid: context.collectionid, event: event, noteid: item.noteid, notetext: item.text, service: "Tesseract", callback: function(value: any){ 17 | if(context) 18 | { 19 | context.setTranslationDialogState?.(value); 20 | } 21 | }} 22 | } 23 | }, 24 | } 25 | 26 | export default NoteImageMenu; -------------------------------------------------------------------------------- /app/chrome/content/xhtml/preferences/parts/dropbox.xhtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
Client ID
Client secret
Refresh token
Access token
Access token
25 |
-------------------------------------------------------------------------------- /app/chrome/content/xhtml/annotationeditor.xhtml: -------------------------------------------------------------------------------- 1 | 2 | %editMenuOverlayDTD; 4 | %standaloneDTD; 5 | %zoteroDTD; 6 | ]> 7 | 8 | 9 | 10 | 11 | Annotation Comment Editor 12 | 25 | 26 | 27 |
28 | Comments 29 |
30 | 31 | -------------------------------------------------------------------------------- /src/Components/ContextMenu/Menu.module.css: -------------------------------------------------------------------------------- 1 | .menu { 2 | font-size: 0.95em; 3 | } 4 | 5 | @media (prefers-color-scheme: light) { 6 | .menu { 7 | border: solid 1px lightgray; 8 | background-color: #f2f2f2; 9 | } 10 | .ul { 11 | background-color: white; 12 | box-shadow: 0 1px 1px rgba(128, 128, 128, 0.1); 13 | border: solid 1px lightgray; 14 | } 15 | .li { 16 | background-color: white; 17 | } 18 | .lihover { 19 | background-color: #f0f0f0; 20 | } 21 | .label { 22 | 23 | } 24 | .icon { 25 | 26 | } 27 | } 28 | 29 | @media (prefers-color-scheme: dark) { 30 | .menu { 31 | border: solid 1px lightgray; 32 | background-color: #121212; 33 | color: white; 34 | } 35 | .ul { 36 | background-color: #121212; 37 | box-shadow: 0 1px 1px rgba(12, 12, 12, 0.1); 38 | border: solid 1px lightgray; 39 | } 40 | .li { 41 | background-color: #121212; 42 | color: white; 43 | } 44 | .lihover { 45 | background-color: #ccc; 46 | color: white; 47 | } 48 | .label { 49 | color: white; 50 | } 51 | .icon { 52 | color: white; 53 | 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /app/chrome/content/xhtml/preferences/parts/openai.xhtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | API Key 5 | 6 | 7 | 8 | Model 9 | 10 | 11 | 12 | Max token 13 | 14 | 15 | 16 | System message 17 | 18 | 19 | 20 | User prompt 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/Core/ZPrefs.ts: -------------------------------------------------------------------------------- 1 | import Config from "../Config"; 2 | import Crypto from "./Crypto"; 3 | 4 | const ZPrefs = { 5 | prefix: "extensions."+Config.slug+".v1.", 6 | set(key: string, value: string) { 7 | return Zotero.Prefs.set(this.prefix + key, value, true); 8 | }, 9 | 10 | get(key: string, default_value: any = "") { 11 | return Zotero.Prefs.get(this.prefix + key, true) ?? default_value; 12 | }, 13 | 14 | clear(key: string) 15 | { 16 | Zotero.Prefs.clear(this.prefix + key, true); 17 | }, 18 | 19 | async setb(key: string, value: string) { 20 | try { 21 | const encryptedValue = Crypto.encrypt(value, Config.znkey); 22 | return this.set(key, encryptedValue); 23 | } catch (e) { 24 | Zotero.log(e); 25 | return false; 26 | } 27 | }, 28 | 29 | async getb(key: string, default_value: string = "") { 30 | const value = this.get(key); 31 | if (!value) { 32 | return default_value; 33 | } 34 | try { 35 | return Crypto.decrypt(value, Config.znkey); 36 | } catch (e) { 37 | Zotero.log(e); 38 | return default_value; 39 | } 40 | }, 41 | }; 42 | 43 | export default ZPrefs; 44 | -------------------------------------------------------------------------------- /src/Components/Table.module.css: -------------------------------------------------------------------------------- 1 | .legacytbody fieldset { 2 | border: none!important; 3 | margin: 0; 4 | } 5 | 6 | .legacytbody legend { 7 | all: unset; 8 | display: block; 9 | font-weight: bold; 10 | margin: 0; 11 | padding: 0; 12 | margin-bottom: 0.2em; 13 | 14 | } 15 | 16 | .legacytbody legend[data-legend="Main comment"] { 17 | display: none; 18 | } 19 | 20 | .legacytbody legend img { 21 | display: none; 22 | } 23 | 24 | .hidemainlegend :global(.main-legend) { 25 | display: none; 26 | } 27 | 28 | .hidemainlegend :global(.main-fieldset) { 29 | border: none!important; 30 | margin: 0!important; 31 | padding: 0!important; 32 | } 33 | 34 | legend[data-legend=""] { 35 | display: none; 36 | } 37 | 38 | fieldset[data-legend=""] { 39 | display: none; 40 | } 41 | 42 | .tbody { 43 | 44 | } 45 | 46 | 47 | @media (prefers-color-scheme: light) { 48 | .tr td, .tr th { 49 | 50 | } 51 | } 52 | 53 | @media (prefers-color-scheme: dark) { 54 | .tr td, .tr th { 55 | background-color :#121212!important; 56 | color: white!important; 57 | } 58 | .tr td { 59 | color: red; 60 | } 61 | 62 | header { 63 | color: white; 64 | } 65 | } 66 | 67 | -------------------------------------------------------------------------------- /app/chrome/content/xhtml/preferences/parts/gemini.xhtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | API Key 5 | 6 | 7 | 8 | Model 9 | 10 | 11 | 17 | 18 | System message 19 | 20 | 21 | 22 | User prompt 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /app/chrome/content/xhtml/preferences/parts/deepseek.xhtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | API Key 5 | 6 | 7 | 8 | Model 9 | 10 | 11 | 17 | 18 | System message 19 | 20 | 21 | 22 | User prompt 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /app/chrome/core/migrate/migrate.js: -------------------------------------------------------------------------------- 1 | if (typeof Services == 'undefined') { 2 | var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); 3 | } 4 | var CryptoJS = null; 5 | 6 | var Migrate = { 7 | version: "0to1", 8 | znkey: "W0phdmFTY3JpcHQgRXJyb3I6ICJSZWZlcmVuY2VFcnJvcjogZmV0Y2ggaXMgbm90IGRlZmluZWQiIHtmaWxlOiAicmVzb3VyY2U6Ly9ncm", 9 | init(rootURI, data) { 10 | Services.scriptloader.loadSubScript(rootURI + '/chrome/core/migrate/aes.js'); 11 | this.CryptoJS = CryptoJS; 12 | this.data = data; 13 | }, 14 | encrypt(value) 15 | { 16 | var bvalue = this.CryptoJS.AES.encrypt(value, this.znkey); 17 | return bvalue.toString(); 18 | }, 19 | decrypt(value) 20 | { 21 | return this.CryptoJS.AES.decrypt(value, this.znkey).toString(this.CryptoJS.enc.Utf8); 22 | }, 23 | 24 | async run() 25 | { 26 | const {prefix, data} = this.data; 27 | for (const [k, { key, encrypt }] of Object.entries(data)) 28 | { 29 | const fullkey = prefix+k; 30 | let v = Zotero.Prefs.get(fullkey, true); 31 | if(encrypt) 32 | { 33 | v = this.decrypt(v); 34 | } 35 | data[k]["value"] = v; 36 | } 37 | await Zotero.AppBase.Engine.Core.Migrator.migrateprefs(data); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/chrome/content/xhtml/browser.xhtml: -------------------------------------------------------------------------------- 1 | 2 | %editMenuOverlayDTD; 4 | %standaloneDTD; 5 | %zoteroDTD; 6 | ]> 7 | 8 | 9 | 10 | 11 | File browser 12 | 25 | 31 | 32 | 33 | 34 |
35 | Comments 36 |
37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/Components/Tabs/Tabs.module.css: -------------------------------------------------------------------------------- 1 | @media (prefers-color-scheme: light) { 2 | .tablinks button { 3 | background-color: #f1f1f1; 4 | } 5 | 6 | .tablinks button:hover { 7 | background-color: #ddd; 8 | } 9 | 10 | .tablinks button.active { 11 | background-color: #ddd; 12 | } 13 | } 14 | 15 | @media (prefers-color-scheme: dark) { 16 | .tablinks button { 17 | background-color: #333; 18 | color: #fff; 19 | } 20 | 21 | .tablinks button:hover { 22 | background-color: #444; 23 | } 24 | 25 | .tablinks button.active { 26 | background-color: #555; 27 | } 28 | } 29 | 30 | .sep { 31 | flex: 1; 32 | } 33 | 34 | .tabs { 35 | display: flex; 36 | flex-direction: column; 37 | max-height: 100%; 38 | } 39 | 40 | .tablinks { 41 | display: flex; 42 | justify-content: space-around; 43 | border-radius: 0.3em; 44 | } 45 | 46 | .tablinks button { 47 | padding: 10px; 48 | cursor: pointer; 49 | text-align: left; 50 | border: none; 51 | white-space: nowrap; 52 | transition: background-color 0.3s ease; 53 | } 54 | 55 | 56 | .tabcontent { 57 | padding: 20px; 58 | } 59 | 60 | .tabcontent.active { 61 | display: block; 62 | } 63 | 64 | .container { 65 | height: 100%; 66 | overflow-y: auto; 67 | background-color: gold; 68 | } 69 | -------------------------------------------------------------------------------- /src/Core/Ai/Base.ts: -------------------------------------------------------------------------------- 1 | import Request from "../Request"; 2 | 3 | class Base { 4 | url: string; 5 | method: string; 6 | apikey: string; 7 | model: string; 8 | headers: any; 9 | payload: any; 10 | format: any; 11 | 12 | constructor(url: string, method: string, apikey: string, model: string, headers: any, payload: any, format: any) { 13 | this.url = url; 14 | this.method = method; 15 | this.apikey = apikey; 16 | this.model = model; 17 | this.headers = headers; 18 | this.payload = payload; 19 | this.format = format; 20 | } 21 | 22 | prompt(query: string, data: string) { 23 | let options: any = `{ 24 | "method": "`+this.method+`", 25 | "headers": `+this.headers+`, 26 | "body": `+this.payload+` 27 | }`; 28 | options = options 29 | .replace("${apikey}", this.apikey) 30 | .replace("${model}", this.model) 31 | .replace("${prompts}", query.split("\"").join("\\\"")) 32 | .replace("${data}", data.split("\"").join("\\\"")) 33 | .replace("${headers}", this.headers); 34 | 35 | try { 36 | options = JSON.parse(options); 37 | options["body"] = JSON.stringify(options["body"]); 38 | } catch (e) { 39 | throw e; 40 | } 41 | 42 | return Request.send(this.url, options, this.format); 43 | } 44 | } 45 | 46 | export default Base; 47 | -------------------------------------------------------------------------------- /src/Components/Color.ts: -------------------------------------------------------------------------------- 1 | 2 | const Color = { 3 | addopacity(v: string, opacity: number) 4 | { 5 | let r = v; 6 | const isrgb = v.includes("rgb("); 7 | const isrgba = v.includes("rgba("); 8 | const ishex = v.includes("#"); 9 | if(isrgba) 10 | { 11 | r = v.substring(0, v.lastIndexOf(","))+", "+(opacity/255).toFixed(1)+")"; 12 | } 13 | else if(ishex) 14 | { 15 | r = v.substring(0, 7)+""+Color.tohex(opacity); 16 | } 17 | else if(isrgb) 18 | { 19 | r = v.replace(")", ", "+(opacity/255).toFixed(1)+")"); 20 | r = r.replace("rgb(", "rgba("); 21 | } 22 | return r; 23 | }, 24 | 25 | tohex(opacity: number) 26 | { 27 | const hex = "0123456789ABCDEF"; 28 | return hex.charAt((opacity - opacity % 16)/16) + hex.charAt(opacity % 16) 29 | }, 30 | 31 | rgbaToHex(rgba: string): string { 32 | const result = rgba.match(/rgba?\((\d+),\s*(\d+),\s*(\d+),?\s*([\d\.]+)?\)/); 33 | if (!result) throw new Error("Invalid RGBA format"); 34 | 35 | const r = parseInt(result[1]); 36 | const g = parseInt(result[2]); 37 | const b = parseInt(result[3]); 38 | const a = result[4] !== undefined ? Math.round(parseFloat(result[4]) * 255) : 255; 39 | 40 | const toHex = (n: number) => n.toString(16).padStart(2, '0'); 41 | 42 | return `#${toHex(r)}${toHex(g)}${toHex(b)}${a !== 255 ? toHex(a) : ''}`; 43 | } 44 | } 45 | 46 | export default Color; -------------------------------------------------------------------------------- /src/Components/Tabs/Tabs.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import styles from './Tabs.module.css'; 3 | import TabContent from './TabContent' 4 | 5 | interface Tab { 6 | title: string; 7 | content: React.ComponentType; // The content is a React component 8 | } 9 | 10 | interface TabsProps { 11 | tabs: Tab[]; 12 | } 13 | 14 | const Tabs: React.FC = ({ tabs }) => { 15 | const [activeTab, setActiveTab] = useState(1); 16 | 17 | const handleTabClick = (tabId: number) => { 18 | setActiveTab(tabId); 19 | }; 20 | 21 | return ( 22 |
23 |
24 | {tabs.map((tab, index) => ( 25 | 32 | ))} 33 |
34 |
35 |
36 | {tabs.map((tab, index) => ( 37 | 43 | ))} 44 |
45 |
46 | ); 47 | }; 48 | 49 | export default Tabs; 50 | -------------------------------------------------------------------------------- /src/Components/Dialog/TranslationElement.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | 3 | interface TranslationElementProps { 4 | data: string[]; 5 | save: (value: string) => void; 6 | } 7 | 8 | const TranslationElement: React.FC = ({ data, save }) => { 9 | const [currentText, setCurrentText] = useState(data[0] || ""); 10 | 11 | return ( 12 |
20 |
21 | {data.map((item, index) => ( 22 |