├── .browserslistrc
├── .editorconfig
├── .env.example
├── .eslintrc.js
├── .gitattributes
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── _bug_report_chs.md
│ ├── _feature_request_chs.md
│ ├── _new_dict_chs.md
│ ├── bug_report.md
│ ├── config.yml
│ └── feature_request.md
├── config.yml
└── no-response.yml
├── .gitignore
├── .neutrinorc.js
├── .prettierrc
├── .storybook
├── addons.ts
├── assets
│ └── shewalksinbeauty.mp3
├── config.ts
├── configs
│ └── contexts.tsx
├── manager-head.html
├── preview-head.html
├── style.css
└── webpack.config.js
├── .travis.yml
├── .vscode
├── locales.schema.json
└── settings.json
├── CHANGELOG.md
├── CONTRIBUTING-zh.md
├── CONTRIBUTING.md
├── LICENSE
├── README-zh.md
├── README.md
├── assets
├── content.css
├── default.pdf
├── fanyi.youdao.2.0
│ ├── all-packed.css
│ ├── bar-sp-repeat-x.png
│ ├── bar-sp.png
│ ├── conn.html
│ ├── conn.js
│ ├── logo.png
│ ├── main.js
│ ├── swipe_hr.png
│ ├── switch_button.png
│ ├── switch_button_hover.png
│ ├── trans_tip_submit_bg.png
│ ├── trans_tip_submit_bg_hover.png
│ └── ydd_tip.png
├── google-page-trans.js
├── icon-128.png
├── icon-16.png
├── icon-19.png
├── icon-24.png
├── icon-38.png
├── icon-48.png
├── icon-gray-128.png
├── icon-gray-16.png
├── icon-gray-19.png
├── icon-gray-24.png
├── icon-gray-38.png
├── icon-gray-48.png
├── inject-dict-panel.js
└── vimium-c-injector.js
├── commitlint.config.js
├── config
└── jest
│ ├── cssTransform.js
│ ├── fileTransform.js
│ └── setupTests.js
├── docs
└── saladict.jpg
├── jest.config.js
├── jsconfig.json
├── mac-app
└── Saladict - Pop-up Dictionary and Page Translator
│ ├── Saladict - Pop-up Dictionary and Page Translator Extension
│ ├── Info.plist
│ ├── SafariWebExtensionHandler.swift
│ └── Saladict___Pop_up_Dictionary_and_Page_Translator_Extension.entitlements
│ ├── Saladict - Pop-up Dictionary and Page Translator.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ ├── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ │ └── xcuserdata
│ │ │ └── crimx.xcuserdatad
│ │ │ └── UserInterfaceState.xcuserstate
│ └── xcuserdata
│ │ └── crimx.xcuserdatad
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
│ └── Saladict - Pop-up Dictionary and Page Translator
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ ├── AccentColor.colorset
│ │ └── Contents.json
│ ├── AppIcon.appiconset
│ │ ├── Contents.json
│ │ ├── icon-128.png
│ │ └── icon-16.png
│ └── Contents.json
│ ├── Base.lproj
│ └── Main.storyboard
│ ├── Info.plist
│ ├── Saladict___Pop_up_Dictionary_and_Page_Translator.entitlements
│ └── ViewController.swift
├── package.json
├── postcss.config.js
├── scripts
├── after-build.js
├── build.js
├── firefox-fix.js
├── fixtures.js
├── pdf.js
├── setup-env.js
├── start.js
├── style-extractor.js
└── test.js
├── src
├── _helpers
│ ├── __mocks__
│ │ ├── browser-api.ts
│ │ ├── config-manager.ts
│ │ └── selection.ts
│ ├── analytics
│ │ ├── events.ts
│ │ └── index.ts
│ ├── browser-api.ts
│ ├── check-update.ts
│ ├── chs-to-chz.ts
│ ├── config-manager.ts
│ ├── dom.ts
│ ├── fetch-dom.ts
│ ├── getSuggests.ts
│ ├── hooks.ts
│ ├── i18n.ts
│ ├── injectSaladictInternal.ts
│ ├── integrity.ts
│ ├── lang-check.ts
│ ├── matchPatternToRegExpStr.ts
│ ├── observables.ts
│ ├── permission-manager.ts
│ ├── profile-manager.ts
│ ├── promise-more.ts
│ ├── record-manager.ts
│ ├── saladict.ts
│ ├── scrollbar-width.ts
│ ├── storybook.tsx
│ ├── titlebar-offset.ts
│ ├── translateCtx.ts
│ ├── uniqueKey.ts
│ └── wordoftheday.ts
├── _locales
│ ├── en
│ │ ├── background.ts
│ │ ├── common.ts
│ │ ├── content.ts
│ │ ├── langcode.ts
│ │ ├── menus.ts
│ │ ├── options.ts
│ │ ├── popup.ts
│ │ └── wordpage.ts
│ ├── es
│ │ ├── background.ts
│ │ ├── common.ts
│ │ ├── content.ts
│ │ ├── langcode.ts
│ │ ├── menus.ts
│ │ ├── options.ts
│ │ ├── popup.ts
│ │ └── wordpage.ts
│ ├── manifest
│ │ ├── en
│ │ │ └── messages.json
│ │ ├── np
│ │ │ └── messages.json
│ │ ├── zh_CN
│ │ │ └── messages.json
│ │ └── zh_TW
│ │ │ └── messages.json
│ ├── ne
│ │ ├── background.ts
│ │ ├── common.ts
│ │ ├── content.ts
│ │ ├── langcode.ts
│ │ ├── menus.ts
│ │ ├── options.ts
│ │ ├── popup.ts
│ │ └── wordpage.ts
│ ├── zh-CN
│ │ ├── background.ts
│ │ ├── common.ts
│ │ ├── content.ts
│ │ ├── langcode.ts
│ │ ├── menus.ts
│ │ ├── options.ts
│ │ ├── popup.ts
│ │ └── wordpage.ts
│ └── zh-TW
│ │ ├── background.ts
│ │ ├── common.ts
│ │ ├── content.ts
│ │ ├── langcode.ts
│ │ ├── menus.ts
│ │ ├── options.ts
│ │ ├── popup.ts
│ │ └── wordpage.ts
├── _sass_shared
│ ├── _fancy-scrollbar.scss
│ ├── _global
│ │ ├── _interfaces.scss
│ │ ├── _mixins.scss
│ │ ├── _variables.scss
│ │ └── _z-indices.scss
│ ├── _namespace.scss
│ ├── _reset.scss
│ └── _theme.scss
├── app-config
│ ├── auth.ts
│ ├── context-menus.ts
│ ├── dicts.ts
│ ├── index.ts
│ ├── merge-config.ts
│ ├── merge-profile.ts
│ └── profiles.ts
├── assets
│ ├── Speaker.svg
│ ├── bowl.svg
│ ├── engines
│ │ ├── bing.ico
│ │ ├── howjsay.ico
│ │ ├── ud.ico
│ │ └── vocabulary.ico
│ ├── iconfont.ttf
│ ├── iconfont.woff
│ ├── leaf.svg
│ ├── orange.svg
│ ├── saladict.svg
│ ├── symbol-defs.svg
│ ├── tomato.svg
│ └── wechat.png
├── audio-control
│ ├── audio-control.scss
│ └── index.tsx
├── background
│ ├── __fake__
│ │ └── env.ts
│ ├── __mocks__
│ │ └── database.ts
│ ├── audio-manager.ts
│ ├── badge.ts
│ ├── clipboard-manager.ts
│ ├── context-menus.ts
│ ├── database
│ │ ├── core.ts
│ │ ├── index.ts
│ │ ├── read.ts
│ │ ├── sync-meta.ts
│ │ └── write.ts
│ ├── env.ts
│ ├── i18n-manager.ts
│ ├── index.ts
│ ├── initialization.ts
│ ├── page-translate
│ │ └── caiyun.ts
│ ├── pdf-sniffer.ts
│ ├── server.ts
│ ├── sync-manager
│ │ ├── __mocks__
│ │ │ └── helpers.ts
│ │ ├── helpers.ts
│ │ ├── index.ts
│ │ ├── interface.ts
│ │ └── services
│ │ │ ├── ankiconnect
│ │ │ ├── _locales
│ │ │ │ ├── en.ts
│ │ │ │ ├── zh-CN.ts
│ │ │ │ └── zh-TW.ts
│ │ │ └── index.ts
│ │ │ ├── eudic
│ │ │ ├── _locales
│ │ │ │ ├── en.ts
│ │ │ │ ├── zh-CN.ts
│ │ │ │ └── zh-TW.ts
│ │ │ └── index.ts
│ │ │ ├── shanbay
│ │ │ ├── _locales
│ │ │ │ ├── en.ts
│ │ │ │ ├── zh-CN.ts
│ │ │ │ └── zh-TW.ts
│ │ │ └── index.ts
│ │ │ └── webdav
│ │ │ ├── _locales
│ │ │ ├── en.ts
│ │ │ ├── zh-CN.ts
│ │ │ └── zh-TW.ts
│ │ │ └── index.ts
│ ├── types.ts
│ └── windows-manager.ts
├── components
│ ├── AntdRoot
│ │ ├── AntdRootContainer.tsx
│ │ ├── _style.scss
│ │ └── index.tsx
│ ├── EntryBox
│ │ ├── EntryBox.scss
│ │ ├── EntryBox.stories.tsx
│ │ └── index.tsx
│ ├── ErrorBoundary.tsx
│ ├── FloatBox
│ │ ├── FloatBox.scss
│ │ ├── FloatBox.stories.tsx
│ │ └── index.tsx
│ ├── HoverBox
│ │ ├── HoverBox.scss
│ │ └── index.tsx
│ ├── MachineTrans
│ │ ├── MachineTrans.scss
│ │ ├── MachineTrans.stories.tsx
│ │ ├── MachineTrans.tsx
│ │ └── engine.ts
│ ├── ShadowPortal
│ │ ├── ShadowPortal.scss
│ │ └── index.tsx
│ ├── Speaker
│ │ ├── Speaker.scss
│ │ ├── Speaker.stories.tsx
│ │ └── index.tsx
│ ├── StarRates
│ │ └── index.tsx
│ ├── StrElm
│ │ └── index.tsx
│ ├── Waveform
│ │ ├── Waveform.scss
│ │ ├── Waveform.stories.tsx
│ │ └── Waveform.tsx
│ ├── WordPage
│ │ ├── ExportModal
│ │ │ ├── Linebreak.tsx
│ │ │ ├── PlaceholderTable.tsx
│ │ │ └── index.tsx
│ │ ├── Header.tsx
│ │ ├── WordTable.tsx
│ │ ├── _style.scss
│ │ └── index.tsx
│ └── dictionaries
│ │ ├── ahdict
│ │ ├── View.tsx
│ │ ├── _locales.json
│ │ ├── _style.shadow.scss
│ │ ├── config.ts
│ │ ├── engine.ts
│ │ └── favicon.png
│ │ ├── baidu
│ │ ├── View.tsx
│ │ ├── _locales.ts
│ │ ├── _style.shadow.scss
│ │ ├── auth.ts
│ │ ├── config.ts
│ │ ├── engine.ts
│ │ └── favicon.png
│ │ ├── bing
│ │ ├── View.tsx
│ │ ├── _locales.json
│ │ ├── _style.shadow.scss
│ │ ├── config.ts
│ │ ├── engine.ts
│ │ └── favicon.png
│ │ ├── caiyun
│ │ ├── View.tsx
│ │ ├── _locales.ts
│ │ ├── _style.shadow.scss
│ │ ├── auth.ts
│ │ ├── config.ts
│ │ ├── engine.ts
│ │ └── favicon.png
│ │ ├── cambridge
│ │ ├── View.tsx
│ │ ├── _locales.json
│ │ ├── _style.shadow.scss
│ │ ├── config.ts
│ │ ├── engine.ts
│ │ └── favicon.png
│ │ ├── cnki
│ │ ├── View.tsx
│ │ ├── _locales.json
│ │ ├── _style.shadow.scss
│ │ ├── config.ts
│ │ ├── engine.ts
│ │ └── favicon.png
│ │ ├── cobuild
│ │ ├── View.tsx
│ │ ├── _locales.json
│ │ ├── _style.shadow.scss
│ │ ├── config.ts
│ │ ├── engine.ts
│ │ └── favicon.png
│ │ ├── dictionaries.stories.tsx
│ │ ├── etymonline
│ │ ├── View.tsx
│ │ ├── _locales.json
│ │ ├── _style.shadow.scss
│ │ ├── config.ts
│ │ ├── engine.ts
│ │ └── favicon.png
│ │ ├── eudic
│ │ ├── View.tsx
│ │ ├── _locales.json
│ │ ├── _style.shadow.scss
│ │ ├── config.ts
│ │ ├── engine.ts
│ │ └── favicon.png
│ │ ├── google
│ │ ├── View.tsx
│ │ ├── _locales.ts
│ │ ├── _style.shadow.scss
│ │ ├── config.ts
│ │ ├── engine.ts
│ │ └── favicon.png
│ │ ├── googledict
│ │ ├── View.tsx
│ │ ├── _locales.json
│ │ ├── _style.shadow.scss
│ │ ├── config.ts
│ │ ├── engine.ts
│ │ └── favicon.png
│ │ ├── guoyu
│ │ ├── View.tsx
│ │ ├── _locales.json
│ │ ├── _style.shadow.scss
│ │ ├── config.ts
│ │ ├── engine.ts
│ │ └── favicon.png
│ │ ├── helpers.ts
│ │ ├── hjdict
│ │ ├── View.tsx
│ │ ├── _locales.json
│ │ ├── _style.shadow.scss
│ │ ├── config.ts
│ │ ├── engine.ts
│ │ └── favicon.png
│ │ ├── jikipedia
│ │ ├── View.tsx
│ │ ├── _locales.json
│ │ ├── _style.shadow.scss
│ │ ├── config.ts
│ │ ├── engine.ts
│ │ └── favicon.png
│ │ ├── jukuu
│ │ ├── View.tsx
│ │ ├── _locales.json
│ │ ├── _style.shadow.scss
│ │ ├── config.ts
│ │ ├── engine.ts
│ │ └── favicon.png
│ │ ├── lexico
│ │ ├── View.tsx
│ │ ├── _locales.json
│ │ ├── _style.shadow.scss
│ │ ├── config.ts
│ │ ├── engine.ts
│ │ └── favicon.png
│ │ ├── liangan
│ │ ├── View.tsx
│ │ ├── _locales.json
│ │ ├── _style.shadow.scss
│ │ ├── config.ts
│ │ ├── engine.ts
│ │ └── favicon.png
│ │ ├── locales.ts
│ │ ├── longman
│ │ ├── View.tsx
│ │ ├── _locales.json
│ │ ├── _style.shadow.scss
│ │ ├── config.ts
│ │ ├── engine.ts
│ │ └── favicon.png
│ │ ├── macmillan
│ │ ├── View.tsx
│ │ ├── _locales.json
│ │ ├── _style.shadow.scss
│ │ ├── config.ts
│ │ ├── engine.ts
│ │ └── favicon.png
│ │ ├── merriamwebster
│ │ ├── View.tsx
│ │ ├── _locales.json
│ │ ├── _style.shadow.scss
│ │ ├── config.ts
│ │ ├── engine.ts
│ │ └── favicon.png
│ │ ├── mojidict
│ │ ├── View.tsx
│ │ ├── _locales.json
│ │ ├── _style.shadow.scss
│ │ ├── config.ts
│ │ ├── engine.ts
│ │ └── favicon.png
│ │ ├── naver
│ │ ├── View.tsx
│ │ ├── _locales.json
│ │ ├── _style.shadow.scss
│ │ ├── config.ts
│ │ ├── engine.ts
│ │ └── favicon.png
│ │ ├── oaldict
│ │ ├── View.tsx
│ │ ├── _locales.json
│ │ ├── _style.shadow.scss
│ │ ├── config.ts
│ │ ├── engine.ts
│ │ └── favicon.png
│ │ ├── renren
│ │ ├── View.tsx
│ │ ├── _locales.json
│ │ ├── _style.shadow.scss
│ │ ├── config.ts
│ │ ├── engine.ts
│ │ └── favicon.png
│ │ ├── shanbay
│ │ ├── View.tsx
│ │ ├── _locales.json
│ │ ├── _style.shadow.scss
│ │ ├── config.ts
│ │ ├── engine.ts
│ │ └── favicon.png
│ │ ├── sogou
│ │ ├── View.tsx
│ │ ├── _locales.ts
│ │ ├── _style.shadow.scss
│ │ ├── auth.ts
│ │ ├── config.ts
│ │ ├── engine.ts
│ │ └── favicon.png
│ │ ├── tencent
│ │ ├── View.tsx
│ │ ├── _locales.ts
│ │ ├── _style.shadow.scss
│ │ ├── auth.ts
│ │ ├── config.ts
│ │ ├── engine.ts
│ │ └── favicon.png
│ │ ├── urban
│ │ ├── View.tsx
│ │ ├── _locales.json
│ │ ├── _style.shadow.scss
│ │ ├── config.ts
│ │ ├── engine.ts
│ │ └── favicon.png
│ │ ├── vocabulary
│ │ ├── View.tsx
│ │ ├── _locales.json
│ │ ├── _style.shadow.scss
│ │ ├── config.ts
│ │ ├── engine.ts
│ │ └── favicon.png
│ │ ├── weblio
│ │ ├── View.tsx
│ │ ├── _locales.json
│ │ ├── _style.shadow.scss
│ │ ├── config.ts
│ │ ├── engine.ts
│ │ └── favicon.png
│ │ ├── weblioejje
│ │ ├── View.tsx
│ │ ├── _locales.json
│ │ ├── _style.shadow.scss
│ │ ├── config.ts
│ │ ├── engine.ts
│ │ └── favicon.png
│ │ ├── websterlearner
│ │ ├── View.tsx
│ │ ├── _locales.json
│ │ ├── _style.shadow.scss
│ │ ├── config.ts
│ │ ├── engine.ts
│ │ └── favicon.png
│ │ ├── wikipedia
│ │ ├── View.tsx
│ │ ├── _locales.json
│ │ ├── _style.shadow.scss
│ │ ├── config.ts
│ │ ├── engine.ts
│ │ └── favicon.png
│ │ ├── youdao
│ │ ├── View.tsx
│ │ ├── _locales.json
│ │ ├── _style.shadow.scss
│ │ ├── config.ts
│ │ ├── engine.ts
│ │ └── favicon.png
│ │ ├── youdaotrans
│ │ ├── View.tsx
│ │ ├── _locales.ts
│ │ ├── _style.shadow.scss
│ │ ├── auth.ts
│ │ ├── config.ts
│ │ ├── engine.ts
│ │ └── favicon.png
│ │ └── zdic
│ │ ├── View.tsx
│ │ ├── _locales.json
│ │ ├── _style.shadow.scss
│ │ ├── config.ts
│ │ ├── engine.ts
│ │ └── favicon.png
├── content
│ ├── __fake__
│ │ ├── env-instant-capture.ts
│ │ ├── env-select-text.ts
│ │ └── env.ts
│ ├── _style.scss
│ ├── components
│ │ ├── DictItem
│ │ │ ├── DictItem.scss
│ │ │ ├── DictItem.stories.tsx
│ │ │ ├── DictItem.tsx
│ │ │ ├── DictItemBody.tsx
│ │ │ ├── DictItemContent.shadow.scss
│ │ │ ├── DictItemHead.scss
│ │ │ └── DictItemHead.tsx
│ │ ├── DictList
│ │ │ ├── DictList.container.tsx
│ │ │ ├── DictList.scss
│ │ │ ├── DictList.stories.tsx
│ │ │ └── DictList.tsx
│ │ ├── DictPanel
│ │ │ ├── DictPanel.container.tsx
│ │ │ ├── DictPanel.portal.tsx
│ │ │ ├── DictPanel.scss
│ │ │ ├── DictPanel.shadow.scss
│ │ │ ├── DictPanel.stories.tsx
│ │ │ ├── DictPanel.tsx
│ │ │ ├── DictPanelStandalone.container.tsx
│ │ │ ├── DictPanelStandalone.scss
│ │ │ └── DictPanelStandalone.tsx
│ │ ├── MenuBar
│ │ │ ├── MenuBar.container.tsx
│ │ │ ├── MenuBar.scss
│ │ │ ├── MenuBar.stories.tsx
│ │ │ ├── MenuBar.tsx
│ │ │ ├── MenubarBtns.scss
│ │ │ ├── MenubarBtns.stories.tsx
│ │ │ ├── MenubarBtns.tsx
│ │ │ ├── Profiles.scss
│ │ │ ├── Profiles.stories.tsx
│ │ │ ├── Profiles.tsx
│ │ │ ├── SearchBox.scss
│ │ │ ├── SearchBox.stories.tsx
│ │ │ ├── SearchBox.tsx
│ │ │ ├── Suggest.scss
│ │ │ ├── Suggest.stories.tsx
│ │ │ └── Suggest.tsx
│ │ ├── MtaBox
│ │ │ ├── MtaBox.container.tsx
│ │ │ ├── MtaBox.scss
│ │ │ ├── MtaBox.stories.tsx
│ │ │ └── MtaBox.tsx
│ │ ├── SaladBowl
│ │ │ ├── SaladBowl.container.tsx
│ │ │ ├── SaladBowl.portal.tsx
│ │ │ ├── SaladBowl.shadow.scss
│ │ │ ├── SaladBowl.stories.tsx
│ │ │ └── SaladBowl.tsx
│ │ ├── WaveformBox
│ │ │ ├── WaveformBox.container.tsx
│ │ │ ├── WaveformBox.scss
│ │ │ ├── WaveformBox.stories.tsx
│ │ │ └── WaveformBox.tsx
│ │ └── WordEditor
│ │ │ ├── CtxTransList.scss
│ │ │ ├── CtxTransList.stories.tsx
│ │ │ ├── CtxTransList.tsx
│ │ │ ├── Notes.scss
│ │ │ ├── Notes.tsx
│ │ │ ├── WordCards.scss
│ │ │ ├── WordCards.tsx
│ │ │ ├── WordEditor.container.tsx
│ │ │ ├── WordEditor.portal.tsx
│ │ │ ├── WordEditor.scss
│ │ │ ├── WordEditor.shadow.scss
│ │ │ ├── WordEditor.stories.tsx
│ │ │ ├── WordEditor.tsx
│ │ │ ├── WordEditorPanel.scss
│ │ │ ├── WordEditorPanel.stories.tsx
│ │ │ ├── WordEditorPanel.tsx
│ │ │ └── WordEditorStandalone.container.tsx
│ ├── index.tsx
│ └── redux
│ │ ├── epics
│ │ ├── index.ts
│ │ ├── newSelection.epic.ts
│ │ ├── searchStart.epic.ts
│ │ └── utils.ts
│ │ ├── index.ts
│ │ ├── init.ts
│ │ └── modules
│ │ ├── action-catalog.ts
│ │ ├── action-handlers
│ │ ├── index.ts
│ │ ├── new-selection.ts
│ │ ├── open-qs-panel.ts
│ │ └── search-start.ts
│ │ ├── index.ts
│ │ └── state.ts
├── history
│ ├── env.ts
│ └── index.tsx
├── manifest
│ ├── chrome.manifest.json
│ ├── common.manifest.js
│ ├── edge.manifest.json
│ ├── firefox.manifest.json
│ └── safari.manifest.json
├── notebook
│ ├── env.ts
│ └── index.tsx
├── options
│ ├── __fake__
│ │ └── env.ts
│ ├── _style.scss
│ ├── acknowledgement.ts
│ ├── components
│ │ ├── BtnPreview
│ │ │ ├── PreviewIcon.tsx
│ │ │ ├── _style.scss
│ │ │ └── index.tsx
│ │ ├── Entries
│ │ │ ├── BlackWhiteList.tsx
│ │ │ ├── ContextMenus
│ │ │ │ ├── AddModal.tsx
│ │ │ │ ├── EditeModal.tsx
│ │ │ │ └── index.tsx
│ │ │ ├── DictAuths.tsx
│ │ │ ├── DictPanel.tsx
│ │ │ ├── Dictionaries
│ │ │ │ ├── AllDicts.tsx
│ │ │ │ ├── DictTitle
│ │ │ │ │ ├── _style.scss
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── EditModal.tsx
│ │ │ │ └── index.tsx
│ │ │ ├── General.tsx
│ │ │ ├── ImportExport.tsx
│ │ │ ├── Notebook
│ │ │ │ ├── index.tsx
│ │ │ │ └── sync-services
│ │ │ │ │ ├── ankiconnect.tsx
│ │ │ │ │ ├── eudic.tsx
│ │ │ │ │ ├── shanbay.tsx
│ │ │ │ │ └── webdav.tsx
│ │ │ ├── PDF.tsx
│ │ │ ├── Permissions.tsx
│ │ │ ├── Popup.tsx
│ │ │ ├── Privacy.tsx
│ │ │ ├── Profiles
│ │ │ │ ├── EditNameModal.tsx
│ │ │ │ └── index.tsx
│ │ │ ├── Pronunciation.tsx
│ │ │ ├── QuickSearch
│ │ │ │ ├── StandaloneModal.tsx
│ │ │ │ ├── TitlebarOffsetModal.tsx
│ │ │ │ └── index.tsx
│ │ │ └── SearchModes
│ │ │ │ ├── index.tsx
│ │ │ │ └── searchMode.tsx
│ │ ├── EntryError.tsx
│ │ ├── EntrySideBar
│ │ │ ├── _style.scss
│ │ │ └── index.tsx
│ │ ├── Header
│ │ │ ├── HeadInfo
│ │ │ │ ├── AckList.tsx
│ │ │ │ ├── _style.scss
│ │ │ │ └── index.tsx
│ │ │ ├── _style.scss
│ │ │ └── index.tsx
│ │ ├── InputNumberGroup
│ │ │ ├── _style.scss
│ │ │ └── index.tsx
│ │ ├── MainEntry.tsx
│ │ ├── MatchPatternModal
│ │ │ ├── PatternItem.tsx
│ │ │ └── index.tsx
│ │ ├── SaladictForm
│ │ │ ├── SaveBtn.tsx
│ │ │ ├── _style.scss
│ │ │ └── index.tsx
│ │ ├── SaladictModalForm.tsx
│ │ └── SortableList
│ │ │ ├── _style.scss
│ │ │ ├── index.tsx
│ │ │ └── reorder.ts
│ ├── env.ts
│ ├── helpers
│ │ ├── change-entry.ts
│ │ ├── layout.ts
│ │ ├── panel-store.ts
│ │ ├── path-joiner.ts
│ │ ├── upload.ts
│ │ ├── use-check-dict-auth.ts
│ │ └── use-form-dirty.ts
│ └── index.tsx
├── popup
│ ├── Notebook.tsx
│ ├── Popup.tsx
│ ├── __fake__
│ │ ├── _style.scss
│ │ └── env.ts
│ ├── _style.scss
│ ├── env.ts
│ └── index.tsx
├── quick-search
│ ├── env.ts
│ ├── index.tsx
│ └── quick-search.scss
├── selection
│ ├── helper.ts
│ ├── index.ts
│ ├── instant-capture.ts
│ ├── message.ts
│ ├── quick-search.ts
│ └── select-text.ts
├── typings
│ ├── css.d.ts
│ ├── global.d.ts
│ ├── helpers.ts
│ └── message.ts
└── word-editor
│ ├── env.ts
│ ├── index.tsx
│ └── word-editor.scss
├── test
├── helper.ts
└── specs
│ ├── _helpers
│ ├── browser-api.spec.ts
│ ├── check-update.spec.ts
│ ├── chs-to-chz.spec.ts
│ ├── lang-check.spec.ts
│ ├── profile-manager.spec.ts
│ └── promise-more.spec.ts
│ ├── background
│ ├── audio-manager.spec.ts
│ ├── context-menus.spec.ts
│ ├── initialization.spec.ts
│ ├── pdf-sniffer.spec.ts
│ └── sync-manager
│ │ └── services
│ │ ├── ankiconnect.spec.ts
│ │ └── webdav.spec.ts
│ └── components
│ └── dictionaries
│ ├── ahdict
│ ├── fixtures.js
│ └── requests.mock.ts
│ ├── bing
│ ├── engine.spec.ts
│ ├── fixtures.js
│ └── requests.mock.ts
│ ├── cambridge
│ ├── engine.spec.ts
│ ├── fixtures.js
│ └── requests.mock.ts
│ ├── cnki
│ ├── engine.spec.ts
│ ├── fixtures.js
│ └── requests.mock.ts
│ ├── cobuild
│ ├── engine.spec.ts
│ ├── fixtures.js
│ └── requests.mock.ts
│ ├── etymonline
│ ├── engine.spec.ts
│ ├── fixtures.js
│ └── requests.mock.ts
│ ├── eudic
│ ├── engine.spec.ts
│ ├── fixtures.js
│ └── requests.mock.ts
│ ├── googledict
│ ├── fixtures.js
│ └── requests.mock.ts
│ ├── guoyu
│ ├── engine.spec.ts
│ ├── fixtures.js
│ └── requests.mock.ts
│ ├── helpers.ts
│ ├── hjdict
│ ├── fixtures.js
│ └── requests.mock.ts
│ ├── jikipedia
│ ├── engine.spec.ts
│ ├── fixtures.js
│ └── requests.mock.ts
│ ├── jukuu
│ ├── engine.spec.ts
│ ├── fixtures.js
│ └── requests.mock.ts
│ ├── lexico
│ ├── engine.spec.ts
│ ├── fixtures.js
│ └── requests.mock.ts
│ ├── liangan
│ ├── fixtures.js
│ └── requests.mock.ts
│ ├── longman
│ ├── engine.spec.ts
│ ├── fixtures.js
│ └── requests.mock.ts
│ ├── macmillan
│ ├── engine.spec.ts
│ ├── fixtures.js
│ └── requests.mock.ts
│ ├── merriamwebster
│ ├── engine.spec.ts
│ ├── fixtures.js
│ ├── requests.mock.ts
│ └── testCases.ts
│ ├── mojidict
│ ├── fixtures.js
│ └── requests.mock.ts
│ ├── naver
│ ├── engine.spec.ts
│ ├── fixtures.js
│ └── requests.mock.ts
│ ├── oaldict
│ ├── fixtures.js
│ └── requests.mock.ts
│ ├── renren
│ ├── engine.spec.ts
│ ├── fixtures.js
│ └── requests.mock.ts
│ ├── shanbay
│ ├── engine.spec.ts
│ ├── fixtures.js
│ └── requests.mock.ts
│ ├── urban
│ ├── engine.spec.ts
│ ├── fixtures.js
│ └── requests.mock.ts
│ ├── vocabulary
│ ├── engine.spec.ts
│ ├── fixtures.js
│ └── requests.mock.ts
│ ├── weblio
│ ├── engine.spec.ts
│ ├── fixtures.js
│ └── requests.mock.ts
│ ├── weblioejje
│ ├── engine.spec.ts
│ ├── fixtures.js
│ └── requests.mock.ts
│ ├── websterlearner
│ ├── engine.spec.ts
│ ├── fixtures.js
│ └── requests.mock.ts
│ ├── wikipedia
│ ├── engine.spec.ts
│ ├── fixtures.js
│ └── requests.mock.ts
│ ├── youdao
│ ├── engine.spec.ts
│ ├── fixtures.js
│ └── requests.mock.ts
│ └── zdic
│ ├── engine.spec.ts
│ ├── fixtures.js
│ └── requests.mock.ts
├── tsconfig.json
├── webpack.config.js
└── yarn.lock
/.browserslistrc:
--------------------------------------------------------------------------------
1 | Firefox > 67
2 | Chrome >= 63
3 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | # http://api.fanyi.baidu.com/api/trans/product/prodinfo
2 | BAIDU_APPID=
3 | BAIDU_KEY=
4 |
5 | # https://fanyi.caiyunapp.com/#/api
6 | CAIYUN_TOKEN=
7 |
8 | # https://cloud.google.com/translate/
9 | GOOGLE_TOKEN=
10 |
11 | # https://deepi.sogou.com/?from=translatepc
12 | SOGOU_PID=
13 | SOGOU_KEY=
14 |
15 | # https://cloud.tencent.com/document/api/213/30654
16 | TENCENT_SECRETID=
17 | TENCENT_SECRETKEY=
18 |
19 | # http://ai.youdao.com/gw.s
20 | YOUDAO_APPKEY=
21 | YOUDAO_KEY=
22 |
23 | # moji dict
24 | MOJI_ID=
25 |
26 | # Download fixtures
27 | # PROXY_PROTOCAL=socks5
28 | # PROXY_HOST=0.0.0.0
29 | # PROXY_PORT=10808
30 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.min.js binary
2 | /public/** binary
3 |
4 | # For Github language details
5 | /test/**/response/*.html linguist-vendored
6 | /public/** linguist-vendored
7 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: saladict
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: saladict
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: ['https://saladict.crimx.com/support.html']
13 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/_new_dict_chs.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: 词典推荐
3 | about: 请求沙拉查词添加新词典。
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | ## 词典名称以及链接
26 |
27 |
28 |
29 | ## 沙拉查词的已有的词典为什么不能满足?
30 |
31 |
32 |
33 |
34 | ## 单词举例
35 |
41 |
42 |
43 |
44 | ## 截图
45 |
46 |
47 |
48 |
49 | ## 额外信息
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Bug related issue.
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | ## Device info
11 | - OS: [e.g. Window10]
12 | - Browser Version [e.g. Chrome77]
13 | - Saladict Version [e.g. v7.0.0]
14 |
15 | ## Describe the bug
16 |
17 |
18 | ## To Reproduce
19 |
20 | 1. Go to '...'
21 | 2. Click on '....'
22 | 3. Scroll down to '....'
23 | 4. See error
24 |
25 | ## Expected behavior
26 |
27 |
28 | ## Screenshots
29 |
30 |
31 | ## Additional context
32 |
33 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for Saladict
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 |
12 |
13 | **Describe the solution you'd like**
14 |
15 |
16 | **Describe alternatives you've considered**
17 |
18 |
19 | **Additional context**
20 |
21 |
--------------------------------------------------------------------------------
/.github/no-response.yml:
--------------------------------------------------------------------------------
1 | # Configuration for probot-no-response - https://github.com/probot/no-response
2 |
3 | # Number of days of inactivity before an Issue is closed for lack of response
4 | daysUntilClose: 14
5 | # Label requiring a response
6 | responseRequiredLabel: needs-more-info
7 | # Comment to post when closing an Issue for lack of response. Set to `false` to disable
8 | closeComment: >
9 | This issue has been automatically closed because there has been no response
10 | to our request for more information from the original author. With only the
11 | information that is currently in the issue, we don't have enough information
12 | to take action. Please reach out if you have or find the answers we need so
13 | that we can investigate further.
14 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | tabWidth: 2
2 | semi: false
3 | singleQuote: true
4 |
--------------------------------------------------------------------------------
/.storybook/addons.ts:
--------------------------------------------------------------------------------
1 | import '@storybook/addon-knobs/register'
2 | import '@storybook/addon-contexts/register'
3 | import '@storybook/addon-actions/register'
4 | import '@storybook/addon-backgrounds/register'
5 | import 'storybook-addon-jsx/register'
6 | import 'storybook-addon-react-docgen/register'
7 |
8 | import addons from '@storybook/addons'
9 | import { STORY_RENDERED } from '@storybook/core-events'
10 |
11 | addons.register('TitleAddon', api => {
12 | api.on(STORY_RENDERED, () => {
13 | const storyData = api.getCurrentStoryData()
14 | document.title = `${storyData.name} - Saladict Storybook`
15 | })
16 | })
17 |
--------------------------------------------------------------------------------
/.storybook/assets/shewalksinbeauty.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/.storybook/assets/shewalksinbeauty.mp3
--------------------------------------------------------------------------------
/.storybook/manager-head.html:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/.storybook/preview-head.html:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/.storybook/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | width: unset;
3 | height: unset;
4 | overflow-y: scroll;
5 | margin: 0;
6 | padding: 0;
7 | box-sizing: border-box;
8 | }
9 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 'stable'
4 | script:
5 | - yarn lint
6 | - yarn build
7 | # remove agnostic tests
8 | - yarn test --testPathIgnorePatterns 'components/dictionaries'
9 |
--------------------------------------------------------------------------------
/.vscode/locales.schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "object",
3 | "properties": {
4 | "name": {
5 | "$ref": "#/definitions/locale"
6 | },
7 | "options": {
8 | "$ref": "#/definitions/locales"
9 | },
10 | "helps": {
11 | "$ref": "#/definitions/locales"
12 | }
13 | },
14 | "required": ["name"],
15 | "additionalProperties": false,
16 |
17 | "definitions": {
18 | "locale": {
19 | "type": "object",
20 | "properties": {
21 | "en": { "type": "string" },
22 | "zh-CN": { "type": "string" },
23 | "zh-TW": { "type": "string" }
24 | },
25 | "required": ["en", "zh-CN", "zh-TW"],
26 | "additionalProperties": false
27 | },
28 | "locales": {
29 | "type": "object",
30 | "patternProperties": {
31 | ".+": {
32 | "$ref": "#/definitions/locale"
33 | }
34 | }
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "json.schemas": [
3 | {
4 | "fileMatch": ["src/components/dictionaries/**/_locales.json"],
5 | "url": ".vscode/locales.schema.json"
6 | }
7 | ],
8 | "files.watcherExclude": {
9 | "**/.git/objects/**": true,
10 | "**/.git/subtree-cache/**": true,
11 | "**/node_modules/*/**": true,
12 | "**/build/*/**": true,
13 | "**/assets/*/**": true
14 | },
15 | "conventionalCommits.scopes": [
16 | "audio-control",
17 | "background",
18 | "components",
19 | "config",
20 | "dicts",
21 | "history",
22 | "locales",
23 | "notebook",
24 | "options",
25 | "panel",
26 | "popup",
27 | "selecion",
28 | "sync-services",
29 | "word-editor"
30 | ]
31 | }
32 |
--------------------------------------------------------------------------------
/assets/content.css:
--------------------------------------------------------------------------------
1 | .saladict-div,
2 | .saladict-div > .saladict-external,
3 | .saladict-div > .saladict-panel {
4 | display: block !important;
5 | width: 0 !important;
6 | height: 0 !important;
7 | margin: 0 !important;
8 | padding: 0 !important;
9 | border: none !important;
10 | outline: none !important;
11 | }
12 |
--------------------------------------------------------------------------------
/assets/default.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/assets/default.pdf
--------------------------------------------------------------------------------
/assets/fanyi.youdao.2.0/bar-sp-repeat-x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/assets/fanyi.youdao.2.0/bar-sp-repeat-x.png
--------------------------------------------------------------------------------
/assets/fanyi.youdao.2.0/bar-sp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/assets/fanyi.youdao.2.0/bar-sp.png
--------------------------------------------------------------------------------
/assets/fanyi.youdao.2.0/conn.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/assets/fanyi.youdao.2.0/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/assets/fanyi.youdao.2.0/logo.png
--------------------------------------------------------------------------------
/assets/fanyi.youdao.2.0/swipe_hr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/assets/fanyi.youdao.2.0/swipe_hr.png
--------------------------------------------------------------------------------
/assets/fanyi.youdao.2.0/switch_button.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/assets/fanyi.youdao.2.0/switch_button.png
--------------------------------------------------------------------------------
/assets/fanyi.youdao.2.0/switch_button_hover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/assets/fanyi.youdao.2.0/switch_button_hover.png
--------------------------------------------------------------------------------
/assets/fanyi.youdao.2.0/trans_tip_submit_bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/assets/fanyi.youdao.2.0/trans_tip_submit_bg.png
--------------------------------------------------------------------------------
/assets/fanyi.youdao.2.0/trans_tip_submit_bg_hover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/assets/fanyi.youdao.2.0/trans_tip_submit_bg_hover.png
--------------------------------------------------------------------------------
/assets/fanyi.youdao.2.0/ydd_tip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/assets/fanyi.youdao.2.0/ydd_tip.png
--------------------------------------------------------------------------------
/assets/icon-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/assets/icon-128.png
--------------------------------------------------------------------------------
/assets/icon-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/assets/icon-16.png
--------------------------------------------------------------------------------
/assets/icon-19.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/assets/icon-19.png
--------------------------------------------------------------------------------
/assets/icon-24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/assets/icon-24.png
--------------------------------------------------------------------------------
/assets/icon-38.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/assets/icon-38.png
--------------------------------------------------------------------------------
/assets/icon-48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/assets/icon-48.png
--------------------------------------------------------------------------------
/assets/icon-gray-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/assets/icon-gray-128.png
--------------------------------------------------------------------------------
/assets/icon-gray-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/assets/icon-gray-16.png
--------------------------------------------------------------------------------
/assets/icon-gray-19.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/assets/icon-gray-19.png
--------------------------------------------------------------------------------
/assets/icon-gray-24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/assets/icon-gray-24.png
--------------------------------------------------------------------------------
/assets/icon-gray-38.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/assets/icon-gray-38.png
--------------------------------------------------------------------------------
/assets/icon-gray-48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/assets/icon-gray-48.png
--------------------------------------------------------------------------------
/assets/inject-dict-panel.js:
--------------------------------------------------------------------------------
1 | const manifest = browser.runtime.getManifest()
2 | if (manifest.content_scripts) {
3 | for (const script of manifest.content_scripts) {
4 | if (script.js) {
5 | for (const js of script.js) {
6 | const $script = document.createElement('script')
7 | $script.type = 'text/javascript'
8 | $script.src = /^\/|([a-z-]+:\/\/)/i.test(js) ? js : `/${js}`
9 | document.body.appendChild($script)
10 | }
11 | }
12 | if (script.css) {
13 | for (const css of script.css) {
14 | const $link = document.createElement('link')
15 | $link.rel = 'stylesheet'
16 | $link.href = /^\/|([a-z-]+:\/\/)/i.test(css) ? css : `/${css}`
17 | document.head.appendChild($link)
18 | }
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/commitlint.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ['@commitlint/config-conventional']
3 | }
4 |
--------------------------------------------------------------------------------
/config/jest/cssTransform.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | // This is a custom Jest transformer turning style imports into empty objects.
4 | // http://facebook.github.io/jest/docs/tutorial-webpack.html
5 |
6 | module.exports = {
7 | process() {
8 | return 'module.exports = {};'
9 | },
10 | getCacheKey() {
11 | // The output is always the same.
12 | return 'cssTransform'
13 | },
14 | }
15 |
--------------------------------------------------------------------------------
/config/jest/fileTransform.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const path = require('path')
4 |
5 | // This is a custom Jest transformer turning file imports into filenames.
6 | // http://facebook.github.io/jest/docs/tutorial-webpack.html
7 |
8 | module.exports = {
9 | process(src, filename) {
10 | return `module.exports = ${JSON.stringify(path.basename(filename))};`
11 | },
12 | }
13 |
--------------------------------------------------------------------------------
/config/jest/setupTests.js:
--------------------------------------------------------------------------------
1 | import browser from 'sinon-chrome/extensions'
2 | // import Enzyme from 'enzyme'
3 | // import Adapter from 'enzyme-adapter-react-16'
4 | import raf from 'raf'
5 | import fetch from 'node-fetch'
6 | import axios from 'axios'
7 |
8 | // force http in jsdom
9 | axios.defaults.adapter = require('axios/lib/adapters/http')
10 |
11 | window.browser = browser
12 | window.Request = fetch.Request
13 | window.Response = fetch.Response
14 | window.Headers = fetch.Headers
15 |
16 | if (process.env.CI) {
17 | window.FormData = require('form-data')
18 | window.fetch = fetch
19 |
20 | jest.setTimeout(30000)
21 | }
22 |
23 | // Enzyme.configure({ adapter: new Adapter() })
24 |
25 | // In tests, polyfill requestAnimationFrame since jsdom doesn't provide it yet.
26 | // We don't polyfill it in the browser--this is user's responsibility.
27 | raf.polyfill(global)
28 |
--------------------------------------------------------------------------------
/docs/saladict.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/docs/saladict.jpg
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | const neutrino = require('neutrino')
2 |
3 | process.env.NODE_ENV = process.env.NODE_ENV || 'test'
4 |
5 | module.exports = neutrino().jest()
6 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": "./",
4 | "paths": {
5 | "@/*": ["src/*"]
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/mac-app/Saladict - Pop-up Dictionary and Page Translator/Saladict - Pop-up Dictionary and Page Translator Extension/Saladict___Pop_up_Dictionary_and_Page_Translator_Extension.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.files.user-selected.read-only
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/mac-app/Saladict - Pop-up Dictionary and Page Translator/Saladict - Pop-up Dictionary and Page Translator.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/mac-app/Saladict - Pop-up Dictionary and Page Translator/Saladict - Pop-up Dictionary and Page Translator.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/mac-app/Saladict - Pop-up Dictionary and Page Translator/Saladict - Pop-up Dictionary and Page Translator.xcodeproj/project.xcworkspace/xcuserdata/crimx.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/mac-app/Saladict - Pop-up Dictionary and Page Translator/Saladict - Pop-up Dictionary and Page Translator.xcodeproj/project.xcworkspace/xcuserdata/crimx.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/mac-app/Saladict - Pop-up Dictionary and Page Translator/Saladict - Pop-up Dictionary and Page Translator.xcodeproj/xcuserdata/crimx.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Saladict - Pop-up Dictionary and Page Translator.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/mac-app/Saladict - Pop-up Dictionary and Page Translator/Saladict - Pop-up Dictionary and Page Translator/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Saladict - Pop-up Dictionary and Page Translator
4 | //
5 | // Created by Jack Wong on 2021/5/31.
6 | //
7 |
8 | import Cocoa
9 |
10 | @main
11 | class AppDelegate: NSObject, NSApplicationDelegate {
12 |
13 | func applicationDidFinishLaunching(_ notification: Notification) {
14 | // Insert code here to initialize your application
15 | }
16 |
17 | func applicationWillTerminate(_ notification: Notification) {
18 | // Insert code here to tear down your application
19 | }
20 |
21 | func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
22 | return true
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/mac-app/Saladict - Pop-up Dictionary and Page Translator/Saladict - Pop-up Dictionary and Page Translator/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/mac-app/Saladict - Pop-up Dictionary and Page Translator/Saladict - Pop-up Dictionary and Page Translator/Assets.xcassets/AppIcon.appiconset/icon-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/mac-app/Saladict - Pop-up Dictionary and Page Translator/Saladict - Pop-up Dictionary and Page Translator/Assets.xcassets/AppIcon.appiconset/icon-128.png
--------------------------------------------------------------------------------
/mac-app/Saladict - Pop-up Dictionary and Page Translator/Saladict - Pop-up Dictionary and Page Translator/Assets.xcassets/AppIcon.appiconset/icon-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/mac-app/Saladict - Pop-up Dictionary and Page Translator/Saladict - Pop-up Dictionary and Page Translator/Assets.xcassets/AppIcon.appiconset/icon-16.png
--------------------------------------------------------------------------------
/mac-app/Saladict - Pop-up Dictionary and Page Translator/Saladict - Pop-up Dictionary and Page Translator/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/mac-app/Saladict - Pop-up Dictionary and Page Translator/Saladict - Pop-up Dictionary and Page Translator/Saladict___Pop_up_Dictionary_and_Page_Translator.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.files.user-selected.read-only
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = () => ({
2 | plugins: [
3 | require('postcss-flexbugs-fixes'),
4 | require('autoprefixer')({
5 | browsers: ['Chrome >= 55', 'Firefox >= 56']
6 | })
7 | ]
8 | })
9 |
--------------------------------------------------------------------------------
/scripts/after-build.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs-extra')
2 | const path = require('path')
3 |
4 | module.exports = class AfterBuildPlugin {
5 | apply(compiler) {
6 | compiler.hooks.done.tapAsync(
7 | 'AfterBuildPlugin',
8 | (compilation, callback) => {
9 | firefoxFix().then(callback)
10 | }
11 | )
12 | }
13 | }
14 |
15 | async function firefoxFix() {
16 | await removeYoudaoFanyi()
17 | await removeCaiyun()
18 | }
19 |
20 | async function removeYoudaoFanyi() {
21 | // FF policy
22 | await fs.remove(
23 | path.join(__dirname, '../build/firefox/assets/fanyi.youdao.2.0')
24 | )
25 | // Stop FF extension check errors
26 | await fs.outputFile(
27 | path.join(__dirname, '../build/firefox/assets/fanyi.youdao.2.0/main.js'),
28 | ''
29 | )
30 | }
31 |
32 | async function removeCaiyun() {
33 | // FF policy
34 | // caiyun trs is close-sourced
35 | await fs.remove(path.join(__dirname, '../build/firefox/assets/trs.js'))
36 | }
37 |
--------------------------------------------------------------------------------
/src/_helpers/__mocks__/selection.ts:
--------------------------------------------------------------------------------
1 | export interface SelectionMock {
2 | hasSelection: jest.Mock
3 | getSelectionText: jest.Mock
4 | getSelectionSentence: jest.Mock
5 | getSelectionInfo: jest.Mock
6 | getDefaultSelectionInfo: jest.Mock
7 | }
8 |
9 | module.exports = jest.genMockFromModule('../selection')
10 |
--------------------------------------------------------------------------------
/src/_helpers/analytics/events.ts:
--------------------------------------------------------------------------------
1 | export type GAEventBase = {
2 | category: string
3 | action: string
4 | label?: string
5 | value?: string
6 | }
7 |
8 | type GAEventFactory = T
9 |
10 | export type GAEvent = GAEventFactory<
11 | | {
12 | category: 'Page_Translate'
13 | action: 'Open_Google' | 'Open_Youdao' | 'Open_Caiyun'
14 | label:
15 | | 'From_Browser_Action'
16 | | 'From_Context_Menus'
17 | | 'From_Browser_Shortcut'
18 | }
19 | | {
20 | category: 'PDF_Viewer'
21 | action: 'Open_PDF_Viewer'
22 | label:
23 | | 'From_Browser_Action'
24 | | 'From_Context_Menus'
25 | | 'From_Browser_Shortcut'
26 | }
27 | >
28 |
--------------------------------------------------------------------------------
/src/_helpers/dom.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * xhtml returns small case
3 | */
4 | export function isTagName(node: Node, tagName: string): boolean {
5 | return (
6 | ((node as HTMLElement).tagName || '').toLowerCase() ===
7 | tagName.toLowerCase()
8 | )
9 | }
10 |
--------------------------------------------------------------------------------
/src/_helpers/hooks.ts:
--------------------------------------------------------------------------------
1 | import { useRef } from 'react'
2 |
3 | /**
4 | * Equivalent to useCallback(fn, [])
5 | */
6 | export function useFixedCallback(fn: T): T {
7 | return useRef(fn).current
8 | }
9 |
10 | /**
11 | * Equivalent to useMemo(() => value, [])
12 | */
13 | export function useFixedMemo(fn: () => T): T {
14 | const initedRef = useRef(false)
15 | const valueRef = useRef()
16 | if (!initedRef.current) {
17 | initedRef.current = true
18 | valueRef.current = fn()
19 | }
20 | return valueRef.current!
21 | }
22 |
--------------------------------------------------------------------------------
/src/_helpers/integrity.ts:
--------------------------------------------------------------------------------
1 | export const isExtTainted =
2 | browser.runtime.id !== atob('Y2Rvbm5tZmZrZGFvYWpma25vZWVlY21jaGlicG1rbWc=') &&
3 | browser.runtime.id !== atob('c2FsYWRpY3RAY3JpbXguY29t') &&
4 | browser.runtime.id !== atob('aWRnaG9jYmJhaGFmcGZoam5maHBiZmJtcGVncGhtbXA=') &&
5 | /apple/i.test(navigator.vendor)
6 |
--------------------------------------------------------------------------------
/src/_helpers/scrollbar-width.ts:
--------------------------------------------------------------------------------
1 | import memoizeOne from 'memoize-one'
2 |
3 | export const getScrollbarWidth = memoizeOne(() => {
4 | if (typeof document === 'undefined') {
5 | return 0
6 | }
7 |
8 | const strut = document.createElement('div')
9 | const strutStyle = strut.style
10 |
11 | strutStyle.position = 'fixed'
12 | strutStyle.left = '0'
13 | strutStyle.overflowY = 'scroll'
14 | strutStyle.visibility = 'hidden'
15 |
16 | document.body.appendChild(strut)
17 |
18 | const width = strut.getBoundingClientRect().right
19 |
20 | strut.remove()
21 |
22 | return width
23 | })
24 |
25 | export default getScrollbarWidth
26 |
--------------------------------------------------------------------------------
/src/_helpers/uniqueKey.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Generate a unique key
3 | */
4 | export function genUniqueKey(): string {
5 | return (
6 | Date.now()
7 | .toString()
8 | .slice(6) +
9 | Math.random()
10 | .toString()
11 | .slice(2, 8)
12 | )
13 | }
14 |
15 | export function genUniqueKeyThunk() {
16 | return genUniqueKey
17 | }
18 |
19 | export function isGeneratedKey(key: unknown): boolean {
20 | return typeof key === 'string' && /^\d{13}$/.test(key)
21 | }
22 |
--------------------------------------------------------------------------------
/src/_locales/en/background.ts:
--------------------------------------------------------------------------------
1 | import { locale as _locale } from '../zh-CN/background'
2 |
3 | export const locale: typeof _locale = {
4 | app: {
5 | off: 'Saladict disabled. (Quick Search Panel is still available)',
6 | tempOff:
7 | 'Saladict disabled on current tab. (Quick Search Panel is still available)',
8 | unsupported:
9 | 'Embedded Saladict panel is unsupported for current tab. Use Standalone Saladict panel instead.'
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/_locales/en/langcode.ts:
--------------------------------------------------------------------------------
1 | import en from '@opentranslate/languages/locales/en.json'
2 | import { locale as _locale } from '../zh-CN/langcode'
3 |
4 | export const locale: typeof _locale = {
5 | ...en,
6 | default: 'Default',
7 | ne_NP: 'Nepali',
8 | ara: 'Arabic',
9 | 'bs-Latn': 'Bosnian',
10 | bul: 'Bulgarian',
11 | cht: 'Chinese (Traditional)',
12 | dan: 'Danish',
13 | est: 'Estonian',
14 | fin: 'Finnish',
15 | fra: 'French',
16 | iw: 'Hebrew',
17 | jp: 'Japanese',
18 | kor: 'Korean',
19 | kr: 'Korean',
20 | pt_BR: 'Brazilian',
21 | rom: 'Romanian',
22 | slo: 'Slovenian',
23 | spa: 'Spanish',
24 | swe: 'Swedish',
25 | tl: 'Tagalog (Filipino)',
26 | vie: 'Vietnamese',
27 | zh: 'Chinese (Simplified)',
28 | 'zh-CHS': 'Chinese (Simplified)',
29 | 'zh-CHT': 'Chinese (Traditional)'
30 | }
31 |
--------------------------------------------------------------------------------
/src/_locales/en/popup.ts:
--------------------------------------------------------------------------------
1 | import { locale as _locale } from '../zh-CN/popup'
2 |
3 | export const locale: typeof _locale = {
4 | title: 'Saladict Browser Action Panel',
5 | app_active_title: 'Enable Inline Translator',
6 | app_temp_active_title: 'Temporary disabled to the page',
7 | instant_capture_pinned: ' (pinned) ',
8 | instant_capture_title: 'Enable Instant Capture',
9 | notebook_added: 'Added',
10 | notebook_empty: 'No selection found on the current page',
11 | notebook_error: 'Cannot add selected text to Notebook',
12 | page_no_response: 'Page no response',
13 | qrcode_title: 'Qrcode of the page'
14 | }
15 |
--------------------------------------------------------------------------------
/src/_locales/es/background.ts:
--------------------------------------------------------------------------------
1 | import { locale as _locale } from '../zh-CN/background'
2 |
3 | export const locale: typeof _locale = {
4 | app: {
5 | off: 'Saladict desactivado. (El panel de búsqueda rápida sigue disponible)',
6 | tempOff:
7 | 'Saladict desactivado en la pestaña actual. (El panel de búsqueda rápida sigue disponible)',
8 | unsupported:
9 | 'El panel Saladict incrustado no es compatible con la pestaña actual. Utilice el panel Saladict independiente en su lugar.'
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/_locales/es/langcode.ts:
--------------------------------------------------------------------------------
1 | import en from '@opentranslate/languages/locales/en.json'
2 | import { locale as _locale } from '../zh-CN/langcode'
3 |
4 | export const locale: typeof _locale = {
5 | ...en,
6 | default: 'Predeterminado',
7 | ara: 'Arabe',
8 | 'bs-Latn': 'Bosnio',
9 | bul: 'Búlgaro',
10 | cht: 'Chino (Tradicional)',
11 | dan: 'Danés',
12 | est: 'Estonio',
13 | fin: 'Finlandés',
14 | fra: 'Francés',
15 | iw: 'Hebreo',
16 | jp: 'Japonés',
17 | kor: 'Coreano',
18 | kr: 'Coreano',
19 | pt_BR: 'Brasileño',
20 | rom: 'Rumano',
21 | slo: 'Esloveno',
22 | spa: 'Español',
23 | swe: 'Sueco',
24 | tl: 'Tagalo (Filipino)',
25 | vie: 'Vietnamita',
26 | zh: 'Chino (Simplificado)',
27 | 'zh-CHS': 'Chino (Simplificado)',
28 | 'zh-CHT': 'Chino (Tradicional)'
29 | }
30 |
--------------------------------------------------------------------------------
/src/_locales/es/popup.ts:
--------------------------------------------------------------------------------
1 | import { locale as _locale } from '../zh-CN/popup'
2 |
3 | export const locale: typeof _locale = {
4 | title: 'Panel de acciones del navegador de Saladict',
5 | app_active_title: 'Activar el traductor en línea',
6 | app_temp_active_title: 'Desactivación temporal de la página',
7 | instant_capture_pinned: ' (fijado) ',
8 | instant_capture_title: 'Activar captura instantánea',
9 | notebook_added: 'Añadido',
10 | notebook_empty: 'No se ha encontrado ninguna selección en la página actual',
11 | notebook_error: 'No se puede añadir el texto seleccionado al bloc de notas',
12 | page_no_response: 'La pagina no responde',
13 | qrcode_title: 'Qrcode de la página'
14 | }
15 |
--------------------------------------------------------------------------------
/src/_locales/ne/background.ts:
--------------------------------------------------------------------------------
1 | import { locale as _locale } from '../zh-CN/background'
2 |
3 | export const locale: typeof _locale = {
4 | app: {
5 | off: 'सलाडिक्ट असक्षम। (द्रुत खोज प्यानल अझै उपलब्ध छ)',
6 | tempOff:
7 | 'हालको ट्याबमा सलाडिक्ट असक्षम पारियो। (द्रुत खोज प्यानल अझै उपलब्ध छ)',
8 | unsupported:
9 | 'हालको ट्याबका लागि एम्बेडेड सलाडिक प्यानल असमर्थित छ। यसको सट्टा स्ट्यान्डअलोन सलाडिक्ट प्यानल प्रयोग गर्नुहोस्।'
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/_locales/ne/langcode.ts:
--------------------------------------------------------------------------------
1 | import en from '@opentranslate/languages/locales/en.json'
2 | import { locale as _locale } from '../zh-CN/langcode'
3 |
4 | export const locale: typeof _locale = {
5 | ...en,
6 | default: 'पुर्वनिर्धारित',
7 | ne_NP: 'नेपाली',
8 | ara: 'अरबी',
9 | 'bs-Latn': 'बोस्नियाली (ल्याटिन)',
10 | bul: 'बुल्गेरियाली',
11 | cht: 'चिनियाँ (परम्परागत)',
12 | dan: 'डेनिस',
13 | est: 'इस्टोनियाली',
14 | fin: 'फिनिस',
15 | fra: 'फ्रान्सेली',
16 | iw: 'हिब्रु',
17 | jp: 'जापानी',
18 | kor: 'कोरियाली',
19 | kr: 'कोरियाली',
20 | pt_BR: 'पोर्तुगी (ब्राजिल)',
21 | rom: 'रोमानियाली',
22 | slo: 'स्लोभाकियाली',
23 | spa: 'स्पेनिस',
24 | swe: 'स्विडिस',
25 | tl: 'फिलिपिनी (तागालोग)',
26 | vie: 'भियतनामी',
27 | zh: 'चिनियाँ (सरलिकृत)',
28 | 'zh-CHS': 'चिनियाँ (सरलिकृत)',
29 | 'zh-CHT': 'चिनियाँ (परम्परागत)',
30 | }
31 |
--------------------------------------------------------------------------------
/src/_locales/ne/popup.ts:
--------------------------------------------------------------------------------
1 | import { locale as _locale } from '../zh-CN/popup'
2 |
3 | export const locale: typeof _locale = {
4 | title: 'सलाडिक्ट ब्राउजर एक्सन प्यानल',
5 | app_active_title: 'ईनलाइन अनुवादक सक्षम गर्नुहोस्',
6 | app_temp_active_title: 'पृष्ठमा अस्थायी रूपमा असक्षम गरियो',
7 | instant_capture_pinned: ' (ताराङकित)',
8 | instant_capture_title: 'तत्काल क्याप्चर सक्षम गर्नुहोस्',
9 | notebook_added: 'थपियो',
10 | notebook_empty: 'हालको पृष्ठमा कुनै चयन फेला परेन',
11 | notebook_error: 'नोटबुकमा चयन गरिएको पाठ थप्न सकिएन',
12 | page_no_response: 'पृष्ठको कुनै प्रतिक्रिया छैन',
13 | qrcode_title: 'पृष्ठको क्युआर कोड'
14 | }
15 |
--------------------------------------------------------------------------------
/src/_locales/zh-CN/background.ts:
--------------------------------------------------------------------------------
1 | export const locale = {
2 | app: {
3 | off: '沙拉查词已关闭(快捷查词依然可用)',
4 | tempOff: '沙拉查词已对当前标签关闭(快捷查词依然可用)',
5 | unsupported: '内嵌查词面板不支持此类页面(独立窗口查词面板依然可用)'
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/_locales/zh-CN/langcode.ts:
--------------------------------------------------------------------------------
1 | import zhCN from '@opentranslate/languages/locales/zh-CN.json'
2 |
3 | export const locale = {
4 | ...zhCN,
5 | default: '随扩展语言',
6 | ara: '阿拉伯语',
7 | 'bs-Latn': '波斯尼亚语',
8 | bul: '保加利亚语',
9 | cht: '中文(繁体)',
10 | dan: '丹麦语',
11 | est: '爱沙尼亚语',
12 | fin: '芬兰语',
13 | fra: '法语',
14 | iw: '希伯来语',
15 | jp: '日语',
16 | kor: '韩语',
17 | kr: '韩语',
18 | pt_BR: '巴西语',
19 | rom: '罗马尼亚语',
20 | slo: '斯洛文尼亚语',
21 | spa: '西班牙语',
22 | swe: '瑞典语',
23 | tl: '塔加路语(菲律宾语)',
24 | vie: '越南语',
25 | zh: '中文(简体)',
26 | 'zh-CHS': '中文(简体)',
27 | 'zh-CHT': '中文(繁体)'
28 | }
29 |
--------------------------------------------------------------------------------
/src/_locales/zh-CN/popup.ts:
--------------------------------------------------------------------------------
1 | export const locale = {
2 | title: '沙拉查词-右上弹框',
3 | app_active_title: '启用划词',
4 | app_temp_active_title: '对当前页暂时关闭划词',
5 | instant_capture_pinned: '(钉住)',
6 | instant_capture_title: '开启鼠标悬浮取词',
7 | notebook_added: '已添加',
8 | notebook_empty: '当前页面没有发现选词',
9 | notebook_error: '无法添加选词到生词本',
10 | page_no_response: '页面无响应',
11 | qrcode_title: '当前页面二维码'
12 | }
13 |
--------------------------------------------------------------------------------
/src/_locales/zh-TW/background.ts:
--------------------------------------------------------------------------------
1 | import { locale as _locale } from '../zh-CN/background'
2 |
3 | export const locale: typeof _locale = {
4 | app: {
5 | off: '沙拉查詞已關閉(快捷查詞依然可用)',
6 | tempOff: '沙拉查詞已對當前標籤關閉(快捷查詞依然可用)',
7 | unsupported: '內嵌查字介面不支援此類頁面(獨立視窗查字介面依然可用)'
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/_locales/zh-TW/langcode.ts:
--------------------------------------------------------------------------------
1 | import zhTW from '@opentranslate/languages/locales/zh-TW.json'
2 | import { locale as _locale } from '../zh-CN/langcode'
3 |
4 | export const locale: typeof _locale = {
5 | ...zhTW,
6 | default: '同介面語言',
7 | ara: '阿拉伯語',
8 | 'bs-Latn': '波斯尼亞語',
9 | bul: '保加利亞語',
10 | cht: '中文(繁體)',
11 | dan: '丹麥語',
12 | est: '愛沙尼亞語',
13 | fin: '芬蘭語',
14 | fra: '法語',
15 | iw: '希伯來語',
16 | jp: '日語',
17 | kor: '韓語',
18 | kr: '韓語',
19 | pt_BR: '巴西語',
20 | rom: '羅馬尼亞語',
21 | slo: '斯洛維尼亞語',
22 | spa: '西班牙語',
23 | swe: '瑞典語',
24 | tl: '他加祿語(菲律賓語)',
25 | vie: '越南語',
26 | zh: '中文(簡體)',
27 | 'zh-CHS': '中文(簡體)',
28 | 'zh-CHT': '中文(繁體)'
29 | }
30 |
--------------------------------------------------------------------------------
/src/_locales/zh-TW/popup.ts:
--------------------------------------------------------------------------------
1 | import { locale as _locale } from '../zh-CN/popup'
2 |
3 | export const locale: typeof _locale = {
4 | title: '沙拉查詞-右上彈框',
5 | app_active_title: '啟用滑鼠選字',
6 | app_temp_active_title: '對目前頁面暫時關閉滑鼠選字',
7 | instant_capture_pinned: '(釘選)',
8 | instant_capture_title: '啟用滑鼠懸浮取詞',
9 | notebook_added: '已新增',
10 | notebook_empty: '目前頁面沒有發現選詞',
11 | notebook_error: '無法新增選詞到生字本',
12 | page_no_response: '頁面無回應',
13 | qrcode_title: '目前頁面二維條碼'
14 | }
15 |
--------------------------------------------------------------------------------
/src/_sass_shared/_global/_variables.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/_sass_shared/_global/_variables.scss
--------------------------------------------------------------------------------
/src/_sass_shared/_global/_z-indices.scss:
--------------------------------------------------------------------------------
1 | // Max 2^31 − 1 = 2147483647
2 |
3 | $global-zindex-dropdown-backdrop: 2147483639 !default;
4 | $global-zindex-navbar: 2147483640 !default;
5 | $global-zindex-dropdown: 2147483641 !default;
6 | $global-zindex-fixed: 2147483642 !default;
7 | $global-zindex-sticky: 2147483643 !default;
8 | $global-zindex-modal-backdrop: 2147483644 !default;
9 | $global-zindex-modal: 2147483645 !default;
10 | $global-zindex-popover: 2147483646 !default;
11 | $global-zindex-tooltip: 2147483647 !default;
12 |
13 |
14 | $global-zindex-dicteditor: 2147483646;
15 | $global-zindex-dictpanel-dragbg: 2147483646;
16 | $global-zindex-dictpanel: 2147483647;
17 | $global-zindex-bowl: 2147483647;
18 |
--------------------------------------------------------------------------------
/src/_sass_shared/_namespace.scss:
--------------------------------------------------------------------------------
1 | @use "sass:math";
2 |
--------------------------------------------------------------------------------
/src/_sass_shared/_reset.scss:
--------------------------------------------------------------------------------
1 | /*-----------------------------------------------*\
2 | Custom reset
3 | \*-----------------------------------------------*/
4 |
5 | @import '~normalize-scss';
6 |
7 | h1,
8 | h2,
9 | h3,
10 | h4,
11 | ul,
12 | li,
13 | button {
14 | margin: 0;
15 | padding: 0;
16 | }
17 |
18 | img {
19 | display: block;
20 | max-width: 95%;
21 | }
22 |
23 | p {
24 | margin: 0.5em 0;
25 | }
26 |
27 | ul li {
28 | list-style-type: none;
29 | }
30 |
31 | button {
32 | background: transparent;
33 | border: none;
34 |
35 | &:hover {
36 | outline: none;
37 | }
38 | }
39 |
40 | a {
41 | color: #f9690e;
42 | text-decoration: none;
43 |
44 | &:hover {
45 | text-decoration: underline;
46 | }
47 | }
48 |
49 | select {
50 | color: #666;
51 | border: 1px solid rgba(133, 133, 133, 0.28);
52 | background: transparent;
53 | }
54 |
--------------------------------------------------------------------------------
/src/_sass_shared/_theme.scss:
--------------------------------------------------------------------------------
1 | .saladict-theme {
2 | background-color: #fff;
3 | color: #333;
4 | --color-brand: #5caf9e;
5 | --color-background: #fff;
6 | --color-rgb-background: 255, 255, 255;
7 | --color-font: #333;
8 | --color-font-grey: #666;
9 | --color-divider: #ddd;
10 |
11 | @include isDarkMode {
12 | background-color: #222;
13 | color: #ddd;
14 | --color-brand: #1e947e;
15 | --color-background: #222;
16 | --color-rgb-background: 34, 34, 34;
17 | --color-font: #ddd;
18 | --color-font-grey: #aaa;
19 | --color-divider: #4d4748;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/app-config/auth.ts:
--------------------------------------------------------------------------------
1 | import { auth as baidu } from '@/components/dictionaries/baidu/auth'
2 | import { auth as caiyun } from '@/components/dictionaries/caiyun/auth'
3 | import { auth as sogou } from '@/components/dictionaries/sogou/auth'
4 | import { auth as tencent } from '@/components/dictionaries/tencent/auth'
5 | import { auth as youdaotrans } from '@/components/dictionaries/youdaotrans/auth'
6 |
7 | export const defaultDictAuths = {
8 | baidu,
9 | caiyun,
10 | sogou,
11 | tencent,
12 | youdaotrans
13 | }
14 |
15 | export type DictAuths = typeof defaultDictAuths
16 |
17 | export const getDefaultDictAuths = (): DictAuths =>
18 | JSON.parse(JSON.stringify(defaultDictAuths))
19 |
--------------------------------------------------------------------------------
/src/assets/bowl.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/assets/engines/bing.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/assets/engines/bing.ico
--------------------------------------------------------------------------------
/src/assets/engines/howjsay.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/assets/engines/howjsay.ico
--------------------------------------------------------------------------------
/src/assets/engines/ud.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/assets/engines/ud.ico
--------------------------------------------------------------------------------
/src/assets/engines/vocabulary.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/assets/engines/vocabulary.ico
--------------------------------------------------------------------------------
/src/assets/iconfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/assets/iconfont.ttf
--------------------------------------------------------------------------------
/src/assets/iconfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/assets/iconfont.woff
--------------------------------------------------------------------------------
/src/assets/leaf.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/assets/orange.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/src/assets/tomato.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/assets/wechat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/assets/wechat.png
--------------------------------------------------------------------------------
/src/audio-control/audio-control.scss:
--------------------------------------------------------------------------------
1 | html,
2 | body {
3 | height: 165px;
4 | overflow: hidden;
5 | margin: 0;
6 | padding: 0;
7 | }
8 |
9 | @import '@/_sass_shared/_theme.scss';
10 | @import '@/components/Waveform/Waveform.scss';
11 |
--------------------------------------------------------------------------------
/src/audio-control/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | import Waveform from '@/components/Waveform/Waveform'
4 |
5 | import './audio-control.scss'
6 |
7 | const searchParams = new URL(document.URL).searchParams
8 |
9 | const darkMode = Boolean(searchParams.get('darkmode'))
10 |
11 | ReactDOM.render(
12 | ,
13 | document.getElementById('root')
14 | )
15 |
--------------------------------------------------------------------------------
/src/background/__fake__/env.ts:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 | import AxiosMockAdapter from 'axios-mock-adapter'
3 |
4 | browser.runtime.sendMessage['_sender'].callsFake(() => ({
5 | tab: {
6 | id: 'saladict-page'
7 | }
8 | }))
9 |
10 | // mock dict search requests
11 | const dictMock = new AxiosMockAdapter(axios)
12 | const dictMockReq = require.context(
13 | '../../../test/specs/components/dictionaries/',
14 | true,
15 | /requests\.mock\.ts$/
16 | )
17 | dictMockReq.keys().forEach(filename => {
18 | const { mockRequest } = dictMockReq(filename)
19 | mockRequest(dictMock)
20 | })
21 | dictMock.onAny().reply(config => {
22 | console.warn(`Unmatch url: ${config.url}`, config)
23 | return [404, {}]
24 | })
25 |
--------------------------------------------------------------------------------
/src/background/__mocks__/database.ts:
--------------------------------------------------------------------------------
1 | export const db = jest.fn()
2 | export const isInNotebook = jest.fn()
3 | export const saveWord = jest.fn()
4 | export const deleteWord = jest.fn()
5 | export const getWordsByText = jest.fn()
6 | export const getAllWords = jest.fn()
7 |
--------------------------------------------------------------------------------
/src/background/database/sync-meta.ts:
--------------------------------------------------------------------------------
1 | import { getDB } from './core'
2 |
3 | export async function getSyncMeta(serviceID: string) {
4 | const db = await getDB()
5 | return db.syncmeta
6 | .where('id')
7 | .equals(serviceID)
8 | .first(record => record && record.json)
9 | .catch(e => {
10 | if (process.env.DEBUG) {
11 | console.error(e)
12 | }
13 | })
14 | }
15 |
16 | export async function setSyncMeta(serviceID: string, text: string) {
17 | const db = await getDB()
18 | return db.syncmeta.put({ id: serviceID, json: text })
19 | }
20 |
21 | export async function deleteSyncMeta(serviceID: string) {
22 | const db = await getDB()
23 | return db.syncmeta.delete(serviceID).catch(e => {
24 | if (process.env.DEBUG) {
25 | console.error(e)
26 | }
27 | })
28 | }
29 |
--------------------------------------------------------------------------------
/src/background/database/write.ts:
--------------------------------------------------------------------------------
1 | import { Word, DBArea } from '@/_helpers/record-manager'
2 | import { Message } from '@/typings/message'
3 | import { getDB } from './core'
4 |
5 | export async function saveWord({
6 | area,
7 | word
8 | }: Message<'SAVE_WORD'>['payload']) {
9 | const db = await getDB()
10 | return db[area].put(word)
11 | }
12 |
13 | export async function saveWords({
14 | area,
15 | words
16 | }: {
17 | area: DBArea
18 | words: Word[]
19 | }) {
20 | if (process.env.DEBUG) {
21 | if (words.length !== new Set(words.map(w => w.date)).size) {
22 | console.error('save Words: duplicate records')
23 | }
24 | }
25 | const db = await getDB()
26 | return db[area].bulkPut(words)
27 | }
28 |
29 | export async function deleteWords({
30 | area,
31 | dates
32 | }: Message<'DELETE_WORDS'>['payload']) {
33 | const db = await getDB()
34 | return Array.isArray(dates) ? db[area].bulkDelete(dates) : db[area].clear()
35 | }
36 |
--------------------------------------------------------------------------------
/src/background/env.ts:
--------------------------------------------------------------------------------
1 | export {}
2 |
3 | window.__SALADICT_INTERNAL_PAGE__ = true
4 | window.__SALADICT_BACKGROUND_PAGE__ = true
5 |
--------------------------------------------------------------------------------
/src/background/sync-manager/__mocks__/helpers.ts:
--------------------------------------------------------------------------------
1 | import { EMPTY, Observable } from 'rxjs'
2 |
3 | const emptyPromise = (): Promise => Promise.resolve()
4 |
5 | export const setSyncConfig = jest.fn(emptyPromise)
6 |
7 | export const getSyncConfig = jest.fn(emptyPromise)
8 |
9 | export const createSyncConfigStream = jest.fn((): Observable => EMPTY)
10 |
11 | export const setMeta = jest.fn(emptyPromise)
12 |
13 | export const getMeta = jest.fn(emptyPromise)
14 |
15 | export const deleteMeta = jest.fn(emptyPromise)
16 |
17 | export const setNotebook = jest.fn(emptyPromise)
18 |
19 | export const getNotebook = jest.fn(emptyPromise)
20 |
--------------------------------------------------------------------------------
/src/background/sync-manager/services/ankiconnect/_locales/en.ts:
--------------------------------------------------------------------------------
1 | import { locale as _locale } from './zh-CN'
2 |
3 | export const locale: typeof _locale = {
4 | title: 'Anki Connect',
5 | error: {
6 | server: 'Cannot connect to Anki Connect. Please make sure Anki is running.',
7 | deck: 'Deck not found in Anki.',
8 | notetype: 'Note type not found in Anki.',
9 | add: 'Failed to add word to Anki.'
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/background/sync-manager/services/ankiconnect/_locales/zh-CN.ts:
--------------------------------------------------------------------------------
1 | export const locale = {
2 | title: 'Anki Connect',
3 | error: {
4 | server: '无法连接 Anki Connect,请确认 Anki 已在运行。',
5 | deck: 'Anki 中没有找到相应牌组。',
6 | notetype: 'Anki 中没有找到相应笔记类型。',
7 | add: '添加单词到 Anki 失败。'
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/background/sync-manager/services/ankiconnect/_locales/zh-TW.ts:
--------------------------------------------------------------------------------
1 | import { locale as _locale } from './zh-CN'
2 |
3 | export const locale: typeof _locale = {
4 | title: 'Anki Connect',
5 | error: {
6 | server: '無法連線 Anki Connect,請確認 Anki 已在執行。',
7 | deck: 'Anki 中沒有找到相應牌組。',
8 | notetype: 'Anki 中沒有找到相應筆記型別。',
9 | add: '新增單詞到 Anki 失敗。'
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/background/sync-manager/services/eudic/_locales/en.ts:
--------------------------------------------------------------------------------
1 | import { locale as _locale } from './zh-CN'
2 |
3 | export const locale: typeof _locale = {
4 | title: 'Eudic Word Syncing',
5 | open: 'Open',
6 | error: {
7 | network:
8 | 'Unable to access the new word book of Eudic, please check the network.',
9 | illegal_token: 'Please set legal Eudic authorization information.',
10 | no_wordbook:
11 | 'Unable to add to the new word book of European dictionary. Please go to the European official website to generate the default new word book first.'
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/background/sync-manager/services/eudic/_locales/zh-CN.ts:
--------------------------------------------------------------------------------
1 | export const locale = {
2 | title: '欧路单词同步',
3 | open: '打开',
4 | error: {
5 | network: '无法访问欧路词典生词本,请检查网络。',
6 | illegal_token: '请设置合法的欧路词典授权信息',
7 | no_wordbook: '无法添加到欧路词典生词本,请先上欧路官网生成默认生词本'
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/background/sync-manager/services/eudic/_locales/zh-TW.ts:
--------------------------------------------------------------------------------
1 | import { locale as _locale } from './zh-CN'
2 |
3 | export const locale: typeof _locale = {
4 | title: '歐路單字同步',
5 | open: '開啟',
6 | error: {
7 | network: '無法訪問歐路詞典生詞本,請檢查網絡。',
8 | illegal_token: '請設定合法的歐路詞典授權資訊',
9 | no_wordbook: '無法添加到歐路詞典生詞本,請先上歐路官網生成默認生詞本'
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/background/sync-manager/services/shanbay/_locales/en.ts:
--------------------------------------------------------------------------------
1 | import { locale as _locale } from './zh-CN'
2 |
3 | export const locale: typeof _locale = {
4 | title: 'Shanbay Word Syncing',
5 | open: 'Open',
6 | error: {
7 | login: 'Shanbay login failed. Click to open shanbay.com.',
8 | network:
9 | 'Unable to access shanbay.com. Please check your network connection.',
10 | word:
11 | "Unable to add to Shanbay notebook. This word is not in Shanbay's vocabulary database."
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/background/sync-manager/services/shanbay/_locales/zh-CN.ts:
--------------------------------------------------------------------------------
1 | export const locale = {
2 | title: '扇贝单词同步',
3 | open: '打开',
4 | error: {
5 | login: '扇贝登录已失效,请点击打开官网重新登录。',
6 | network: '无法访问扇贝生词本,请检查网络。',
7 | word: '无法添加到扇贝生词本,扇贝单词库没有收录此单词。'
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/background/sync-manager/services/shanbay/_locales/zh-TW.ts:
--------------------------------------------------------------------------------
1 | import { locale as _locale } from './zh-CN'
2 |
3 | export const locale: typeof _locale = {
4 | title: '扇貝單字同步',
5 | open: '開啟',
6 | error: {
7 | login: '扇貝登入已失效,請點選開啟官網重新登入。',
8 | network: '無法訪問扇貝生字本,請檢查網路。',
9 | word: '無法新增到扇貝生字本,扇貝單字庫沒有收錄此單字。'
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/background/sync-manager/services/webdav/_locales/en.ts:
--------------------------------------------------------------------------------
1 | import { locale as _locale } from './zh-CN'
2 |
3 | export const locale: typeof _locale = {
4 | title: 'WebDAV Word Syncing',
5 | error: {
6 | dir: 'Incorrect "Saladict" directory on server.',
7 | download:
8 | 'Download failed. Unable to connect WebDAV Server. If browser proxy is enabled please adjust rules to bypass WebDAV server.',
9 | internal: 'Unable to save settings.',
10 | missing: 'Missing "Saladict" directory on server.',
11 | mkcol:
12 | 'Cannot create "Saladict" directory on server. Please create the directory manualy on server.',
13 | network: 'Network error. Cannot connect to server.',
14 | parse: 'Incorrect response XML from server.',
15 | unauthorized: 'Incorrect account or password.'
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/background/sync-manager/services/webdav/_locales/zh-CN.ts:
--------------------------------------------------------------------------------
1 | export const locale = {
2 | title: 'WebDAV 单词同步',
3 | error: {
4 | dir: '服务器上“Saladict”目录格式不正确,请检查。',
5 | download:
6 | '下载失败。无法访问 WebDAV 服务器。如启用了浏览器代理请调整规则,不要代理 WebDAV 服务器。',
7 | internal: '无法保存。',
8 | missing: '服务器上缺少“Saladict”目录。',
9 | mkcol: '无法在服务器创建“Saladict”目录。请手动在服务器上创建。',
10 | network: '连接服务器失败。',
11 | parse: '服务器返回 XML 格式不正确。',
12 | unauthorized: '账户或密码不正确。'
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/background/sync-manager/services/webdav/_locales/zh-TW.ts:
--------------------------------------------------------------------------------
1 | import { locale as _locale } from './zh-CN'
2 |
3 | export const locale: typeof _locale = {
4 | title: 'WebDAV 單字同步',
5 | error: {
6 | dir: '伺服器上“Saladict”目錄格式不正確,請檢查。',
7 | download:
8 | '下載失敗。無法訪問 WebDAV 伺服器。如啟用了瀏覽器代理請調整規則,不要代理 WebDAV 伺服器。',
9 | internal: '無法儲存。',
10 | missing: '伺服器上缺少“Saladict”目錄。',
11 | mkcol: '無法在伺服器建立“Saladict”目錄。請手動在伺服器上建立。',
12 | network: '連線伺服器失敗。',
13 | parse: '伺服器返回 XML 格式不正確。',
14 | unauthorized: '帳戶或密碼不正確。'
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/background/types.ts:
--------------------------------------------------------------------------------
1 | import { AppConfig } from '@/app-config'
2 | import { Profile, ProfileIDList } from '@/app-config/profiles'
3 |
4 | declare global {
5 | interface Window {
6 | appConfig: AppConfig
7 | activeProfile: Profile
8 | profileIDList: ProfileIDList
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/components/EntryBox/EntryBox.scss:
--------------------------------------------------------------------------------
1 | .entryBox-Wrap {
2 | padding-top: 0.8em;
3 | }
4 |
5 | .entryBox {
6 | position: relative;
7 | border: 1px solid #c76e06;
8 | border-radius: 5px;
9 | margin-bottom: 1em;
10 | padding: 1em 0.5em 0.5em 0.5em;
11 | }
12 |
13 | .entryBox-Title {
14 | position: absolute;
15 | top: 0;
16 | left: 1em;
17 | transform: translateY(-50%);
18 | max-width: 90%;
19 | overflow: hidden;
20 | padding: 0 5px;
21 | white-space: nowrap;
22 | text-overflow: ellipsis;
23 | font-size: 1.2em;
24 | background: var(--color-background);
25 | }
26 |
--------------------------------------------------------------------------------
/src/components/EntryBox/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC, ComponentProps, ReactNode } from 'react'
2 |
3 | export interface EntryBoxProps extends Omit, 'title'> {
4 | title: ReactNode
5 | }
6 |
7 | /**
8 | * Box-wrapped content
9 | */
10 | export const EntryBox: FC = props => {
11 | const { title, className, children, ...restProps } = props
12 | return (
13 |
17 |
18 | {title}
19 | {children}
20 |
21 |
22 | )
23 | }
24 |
25 | export default EntryBox
26 |
--------------------------------------------------------------------------------
/src/components/ErrorBoundary.tsx:
--------------------------------------------------------------------------------
1 | import React, { ComponentType } from 'react'
2 |
3 | interface ErrorBoundaryProps {
4 | /** Reanders on error */
5 | error?: ComponentType
6 | }
7 |
8 | interface ErrorBoundaryState {
9 | hasError: boolean
10 | }
11 |
12 | export class ErrorBoundary extends React.PureComponent<
13 | ErrorBoundaryProps,
14 | ErrorBoundaryState
15 | > {
16 | state: ErrorBoundaryState = {
17 | hasError: false
18 | }
19 |
20 | static getDerivedStateFromError() {
21 | return { hasError: true }
22 | }
23 |
24 | render() {
25 | return this.state.hasError
26 | ? this.props.error
27 | ? React.createElement(this.props.error)
28 | : null
29 | : this.props.children
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/components/HoverBox/HoverBox.scss:
--------------------------------------------------------------------------------
1 | @import '@/components/FloatBox/FloatBox.scss';
2 |
3 | .hoverBox-Container {
4 | display: inline-block;
5 | position: relative;
6 | }
7 |
8 | .hoverBox-FloatBox {
9 | position: absolute;
10 | z-index: $global-zindex-dictpanel;
11 | }
12 |
13 | .csst-hoverBox {
14 | @include isAnimate(-enter) {
15 | opacity: 0;
16 | transition: opacity 0.4s;
17 | }
18 |
19 | @include isAnimate(-enter-active, -exit) {
20 | opacity: 1;
21 | transition: opacity 0.4s;
22 | }
23 |
24 | @include isAnimate(-exit-active) {
25 | opacity: 0;
26 | transition: opacity 0.4s;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/components/ShadowPortal/ShadowPortal.scss:
--------------------------------------------------------------------------------
1 | .shadowPortal-appear,
2 | .shadowPortal-enter {
3 | opacity: 0;
4 | }
5 | .shadowPortal-appear-active,
6 | .shadowPortal-enter-active {
7 | opacity: 1;
8 | transition: opacity 0.4s;
9 | }
10 |
11 | .shadowPortal-exit {
12 | opacity: 1;
13 | }
14 | .shadowPortal-exit-active {
15 | opacity: 0;
16 | transition: opacity 0.1s;
17 | }
18 |
--------------------------------------------------------------------------------
/src/components/Speaker/Speaker.scss:
--------------------------------------------------------------------------------
1 | $speaker-duration: 1s !default;
2 |
3 | .saladict-Speaker {
4 | display: inline-block;
5 | width: 1.1em;
6 | height: 1.1em;
7 | text-decoration: none;
8 | margin: 0 5px;
9 | padding: 0;
10 | line-height: 1;
11 | vertical-align: text-bottom;
12 | border: none;
13 | background: no-repeat left / cover url('~@/assets/Speaker.svg');
14 | user-select: none;
15 | cursor: pointer;
16 |
17 | &:hover {
18 | outline: none;
19 | }
20 | }
21 |
22 | .saladict-Speaker.isActive {
23 | animation: saladict-Speaker-playing $speaker-duration steps(6) infinite;
24 | }
25 |
26 | @keyframes saladict-Speaker-playing {
27 | from {
28 | background-position-x: 0;
29 | }
30 | 70% {
31 | background-position-x: 100%;
32 | }
33 | 100% {
34 | background-position-x: 100%;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/components/WordPage/ExportModal/Linebreak.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react'
2 | import { Select } from 'antd'
3 | import { TFunction } from 'i18next'
4 |
5 | export type LineBreakOption = '' | 'n' | 'p' | 'br' | 'space'
6 |
7 | export interface LineBreakProps {
8 | t: TFunction
9 | value: LineBreakOption
10 | onChange: (value: LineBreakOption) => void
11 | }
12 |
13 | export const LineBreak: FC = ({ t, value, onChange }) => (
14 |
21 | )
22 |
23 | export const LineBreakMemo = React.memo(LineBreak)
24 |
--------------------------------------------------------------------------------
/src/components/dictionaries/ahdict/_locales.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": {
3 | "en": "Amercian Heritage Dict",
4 | "zh-CN": "美国传统词典",
5 | "zh-TW": "美國傳統詞典"
6 | },
7 | "options": {
8 | "resultnum": {
9 | "en": "Show",
10 | "zh-CN": "结果数量",
11 | "zh-TW": "結果數量"
12 | },
13 | "resultnum_unit": {
14 | "en": "results",
15 | "zh-CN": "个",
16 | "zh-TW": "個"
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/components/dictionaries/ahdict/config.ts:
--------------------------------------------------------------------------------
1 | import { DictItem } from '@/app-config/dicts'
2 |
3 | export type AhdictConfig = DictItem<{
4 | resultnum: number
5 | }>
6 |
7 | export default (): AhdictConfig => ({
8 | lang: '10000000',
9 | selectionLang: {
10 | english: true,
11 | chinese: false,
12 | japanese: false,
13 | korean: false,
14 | french: false,
15 | spanish: false,
16 | deutsch: false,
17 | others: false,
18 | matchAll: false
19 | },
20 | defaultUnfold: {
21 | english: true,
22 | chinese: true,
23 | japanese: true,
24 | korean: true,
25 | french: true,
26 | spanish: true,
27 | deutsch: true,
28 | others: true,
29 | matchAll: false
30 | },
31 | preferredHeight: 240,
32 | selectionWC: {
33 | min: 1,
34 | max: 5
35 | },
36 | options: {
37 | resultnum: 4
38 | }
39 | })
40 |
--------------------------------------------------------------------------------
/src/components/dictionaries/ahdict/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/components/dictionaries/ahdict/favicon.png
--------------------------------------------------------------------------------
/src/components/dictionaries/baidu/View.tsx:
--------------------------------------------------------------------------------
1 | export { MachineTrans as default } from '@/components/MachineTrans/MachineTrans'
2 |
--------------------------------------------------------------------------------
/src/components/dictionaries/baidu/_locales.ts:
--------------------------------------------------------------------------------
1 | import { getMachineLocales } from '../locales'
2 |
3 | export const locales = getMachineLocales({
4 | en: 'Baidu Translate',
5 | 'zh-CN': '百度翻译',
6 | 'zh-TW': '百度翻譯'
7 | })
8 |
--------------------------------------------------------------------------------
/src/components/dictionaries/baidu/_style.shadow.scss:
--------------------------------------------------------------------------------
1 | @import '@/components/MachineTrans/MachineTrans.scss';
2 |
--------------------------------------------------------------------------------
/src/components/dictionaries/baidu/auth.ts:
--------------------------------------------------------------------------------
1 | export const auth = {
2 | appid: '',
3 | key: ''
4 | }
5 |
6 | export const url = 'http://api.fanyi.baidu.com/api/trans/product/prodinfo'
7 |
--------------------------------------------------------------------------------
/src/components/dictionaries/baidu/config.ts:
--------------------------------------------------------------------------------
1 | import {
2 | MachineDictItem,
3 | machineConfig
4 | } from '@/components/MachineTrans/engine'
5 | import { Language } from '@opentranslate/translator'
6 | import { Subunion } from '@/typings/helpers'
7 |
8 | export type BaiduLanguage = Subunion<
9 | Language,
10 | 'zh-CN' | 'zh-TW' | 'en' | 'ja' | 'ko' | 'fr' | 'de' | 'es' | 'ru' | 'nl'
11 | >
12 |
13 | export type BaiduConfig = MachineDictItem
14 |
15 | export default (): BaiduConfig =>
16 | machineConfig(
17 | ['zh-CN', 'zh-TW', 'en', 'ja', 'ko', 'fr', 'de', 'es', 'ru', 'nl'],
18 | {},
19 | {},
20 | {}
21 | )
22 |
--------------------------------------------------------------------------------
/src/components/dictionaries/baidu/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/components/dictionaries/baidu/favicon.png
--------------------------------------------------------------------------------
/src/components/dictionaries/bing/_locales.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": {
3 | "en": "Bing Dict",
4 | "zh-CN": "必应词典",
5 | "zh-TW": "必應詞典"
6 | },
7 | "options": {
8 | "tense": {
9 | "en": "Show sense",
10 | "zh-CN": "显示单词时态",
11 | "zh-TW": "展示單詞時態"
12 | },
13 | "phsym": {
14 | "en": "Show pronunciation",
15 | "zh-CN": "显示单词发音",
16 | "zh-TW": "展示單詞發音"
17 | },
18 | "cdef": {
19 | "en": "Show definitions",
20 | "zh-CN": "显示单词解释",
21 | "zh-TW": "展示單詞解釋"
22 | },
23 | "related": {
24 | "en": "Show related results",
25 | "zh-CN": "失败时显示备选",
26 | "zh-TW": "失敗時顯示備選"
27 | },
28 | "sentence": {
29 | "en": "Show sentences",
30 | "zh-CN": "显示例句",
31 | "zh-TW": "展示例句"
32 | },
33 | "sentence_unit": {
34 | "en": "results",
35 | "zh-CN": "个",
36 | "zh-TW": "個"
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/components/dictionaries/bing/config.ts:
--------------------------------------------------------------------------------
1 | import { DictItem } from '@/app-config/dicts'
2 |
3 | export type BingConfig = DictItem<{
4 | tense: boolean
5 | phsym: boolean
6 | cdef: boolean
7 | related: boolean
8 | sentence: number
9 | }>
10 |
11 | export default (): BingConfig => ({
12 | lang: '11000000',
13 | selectionLang: {
14 | english: true,
15 | chinese: true,
16 | japanese: false,
17 | korean: false,
18 | french: false,
19 | spanish: false,
20 | deutsch: false,
21 | others: false,
22 | matchAll: false
23 | },
24 | defaultUnfold: {
25 | english: true,
26 | chinese: true,
27 | japanese: true,
28 | korean: true,
29 | french: true,
30 | spanish: true,
31 | deutsch: true,
32 | others: true,
33 | matchAll: false
34 | },
35 | preferredHeight: 240,
36 | selectionWC: {
37 | min: 1,
38 | max: 5
39 | },
40 | options: {
41 | tense: true,
42 | phsym: true,
43 | cdef: true,
44 | related: true,
45 | sentence: 4
46 | }
47 | })
48 |
--------------------------------------------------------------------------------
/src/components/dictionaries/bing/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/components/dictionaries/bing/favicon.png
--------------------------------------------------------------------------------
/src/components/dictionaries/caiyun/View.tsx:
--------------------------------------------------------------------------------
1 | export { MachineTrans as default } from '@/components/MachineTrans/MachineTrans'
2 |
--------------------------------------------------------------------------------
/src/components/dictionaries/caiyun/_locales.ts:
--------------------------------------------------------------------------------
1 | import { getMachineLocales } from '../locales'
2 |
3 | export const locales = getMachineLocales({
4 | en: 'LingoCloud',
5 | 'zh-CN': '彩云小译',
6 | 'zh-TW': '彩雲小譯'
7 | })
8 |
--------------------------------------------------------------------------------
/src/components/dictionaries/caiyun/_style.shadow.scss:
--------------------------------------------------------------------------------
1 | @import '@/components/MachineTrans/MachineTrans.scss';
2 |
--------------------------------------------------------------------------------
/src/components/dictionaries/caiyun/auth.ts:
--------------------------------------------------------------------------------
1 | export const auth = {
2 | token: ''
3 | }
4 |
5 | export const url = 'https://fanyi.caiyunapp.com/#/api'
6 |
--------------------------------------------------------------------------------
/src/components/dictionaries/caiyun/config.ts:
--------------------------------------------------------------------------------
1 | import {
2 | MachineDictItem,
3 | machineConfig
4 | } from '@/components/MachineTrans/engine'
5 | import { Language } from '@opentranslate/translator'
6 | import { Subunion } from '@/typings/helpers'
7 |
8 | export type CaiyunLanguage = Subunion
9 |
10 | export type CaiyunConfig = MachineDictItem
11 |
12 | export default (): CaiyunConfig =>
13 | machineConfig(
14 | ['zh-CN', 'en', 'ja'],
15 | {
16 | lang: '11010000'
17 | },
18 | {},
19 | {}
20 | )
21 |
--------------------------------------------------------------------------------
/src/components/dictionaries/caiyun/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/components/dictionaries/caiyun/favicon.png
--------------------------------------------------------------------------------
/src/components/dictionaries/cambridge/_locales.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": {
3 | "en": "Cambridge Dictionary",
4 | "zh-CN": "剑桥词典",
5 | "zh-TW": "劍橋詞典"
6 | },
7 | "options": {
8 | "lang": {
9 | "en": "Language",
10 | "zh-CN": "语言",
11 | "zh-TW": "語言"
12 | },
13 | "lang-default": {
14 | "en": "Default",
15 | "zh-CN": "随扩展",
16 | "zh-TW": "未設定"
17 | },
18 | "lang-en": {
19 | "en": "English",
20 | "zh-CN": "English",
21 | "zh-TW": "English"
22 | },
23 | "lang-en-chs": {
24 | "en": "English–Chinese (Simplified)",
25 | "zh-CN": "英简",
26 | "zh-TW": "英简"
27 | },
28 | "lang-en-chz": {
29 | "en": "English–Chinese (Traditional)",
30 | "zh-CN": "英繁",
31 | "zh-TW": "英繁"
32 | },
33 | "related": {
34 | "en": "Show related results",
35 | "zh-CN": "失败时显示备选",
36 | "zh-TW": "失敗時顯示備選"
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/components/dictionaries/cambridge/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/components/dictionaries/cambridge/favicon.png
--------------------------------------------------------------------------------
/src/components/dictionaries/cnki/_locales.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": {
3 | "en": "CNKI Dict",
4 | "zh-CN": "CNKI翻译助手",
5 | "zh-TW": "CNKI翻譯助手"
6 | },
7 | "options": {
8 | "dict": {
9 | "en": "Show dict result",
10 | "zh-CN": "显示词典结果",
11 | "zh-TW": "展示字典結果"
12 | },
13 | "senbi": {
14 | "en": "Show bilingual sentences",
15 | "zh-CN": "显示双语例句",
16 | "zh-TW": "展示雙語例句"
17 | },
18 | "seneng": {
19 | "en": "Show English sentences",
20 | "zh-CN": "显示英文例句",
21 | "zh-TW": "展示英文例句"
22 | },
23 | "digests": {
24 | "en": "Show relevant digests",
25 | "zh-CN": "显示相关文摘",
26 | "zh-TW": "展示相關文摘"
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/components/dictionaries/cnki/config.ts:
--------------------------------------------------------------------------------
1 | import { DictItem } from '@/app-config/dicts'
2 |
3 | export type CnkiConfig = DictItem<{
4 | dict: boolean
5 | senbi: boolean
6 | seneng: boolean
7 | }>
8 |
9 | export default (): CnkiConfig => ({
10 | lang: '11000000',
11 | selectionLang: {
12 | english: true,
13 | chinese: true,
14 | japanese: false,
15 | korean: false,
16 | french: false,
17 | spanish: false,
18 | deutsch: false,
19 | others: false,
20 | matchAll: false
21 | },
22 | defaultUnfold: {
23 | english: true,
24 | chinese: true,
25 | japanese: true,
26 | korean: true,
27 | french: true,
28 | spanish: true,
29 | deutsch: true,
30 | others: true,
31 | matchAll: false
32 | },
33 | preferredHeight: 300,
34 | selectionWC: {
35 | min: 1,
36 | max: 100
37 | },
38 | options: {
39 | dict: true,
40 | senbi: true,
41 | seneng: true
42 | // digests: true,
43 | }
44 | })
45 |
--------------------------------------------------------------------------------
/src/components/dictionaries/cnki/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/components/dictionaries/cnki/favicon.png
--------------------------------------------------------------------------------
/src/components/dictionaries/cobuild/_locales.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": {
3 | "en": "COBUILD",
4 | "zh-CN": "柯林斯高阶",
5 | "zh-TW": "柯林斯高階"
6 | },
7 | "options": {
8 | "cibaFirst": {
9 | "en": "Prefer bilingual dictionary",
10 | "zh-CN": "优先查双语词典",
11 | "zh-TW": "優先查雙語詞典"
12 | }
13 | },
14 | "helps": {
15 | "cibaFirst": {
16 | "en": "Bilingual server is less stable",
17 | "zh-CN": "双语词典服务器不稳定,解释没全英丰富",
18 | "zh-TW": "雙語詞典伺服器不穩定,解釋沒全英豐富"
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/components/dictionaries/cobuild/config.ts:
--------------------------------------------------------------------------------
1 | import { DictItem } from '@/app-config/dicts'
2 |
3 | export type CobuildConfig = DictItem<{
4 | cibaFirst: boolean
5 | }>
6 |
7 | export default (): CobuildConfig => ({
8 | lang: '10000000',
9 | selectionLang: {
10 | english: true,
11 | chinese: false,
12 | japanese: false,
13 | korean: false,
14 | french: false,
15 | spanish: false,
16 | deutsch: false,
17 | others: false,
18 | matchAll: false
19 | },
20 | defaultUnfold: {
21 | english: true,
22 | chinese: true,
23 | japanese: true,
24 | korean: true,
25 | french: true,
26 | spanish: true,
27 | deutsch: true,
28 | others: true,
29 | matchAll: false
30 | },
31 | preferredHeight: 300,
32 | selectionWC: {
33 | min: 1,
34 | max: 5
35 | },
36 | options: {
37 | cibaFirst: (browser.i18n.getUILanguage() || 'en').startsWith('zh-')
38 | }
39 | })
40 |
--------------------------------------------------------------------------------
/src/components/dictionaries/cobuild/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/components/dictionaries/cobuild/favicon.png
--------------------------------------------------------------------------------
/src/components/dictionaries/etymonline/_locales.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": {
3 | "en": "Etymonline",
4 | "zh-CN": "Etymonline",
5 | "zh-TW": "Etymonline"
6 | },
7 | "options": {
8 | "resultnum": {
9 | "en": "Show",
10 | "zh-CN": "结果数量",
11 | "zh-TW": "結果數量"
12 | },
13 | "resultnum_unit": {
14 | "en": "results",
15 | "zh-CN": "个",
16 | "zh-TW": "個"
17 | },
18 | "chart": {
19 | "en": "Show Chart",
20 | "zh-CN": "显示图表",
21 | "zh-TW": "顯示圖表"
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/components/dictionaries/etymonline/_style.shadow.scss:
--------------------------------------------------------------------------------
1 | .dictEtymonline-List {
2 | p {
3 | margin: 0 0 5px 0;
4 | }
5 |
6 | blockquote {
7 | margin: 0.5em 0;
8 | padding: 0 1em;
9 | font-style: italic;
10 | border-left: 2px solid #f9690e;
11 | }
12 |
13 | .foreign {
14 | font-style: italic;
15 | }
16 |
17 | .line-break {
18 | margin-bottom: 5px;
19 | }
20 | }
21 |
22 | .dictEtymonline-Item {
23 | margin: 10px 0;
24 | }
25 |
26 | .dictEtymonline-Title {
27 | margin: 0;
28 | font-size: 1em;
29 | }
30 |
31 | .dictEtymonline-Def {
32 | margin: 0;
33 | }
34 |
--------------------------------------------------------------------------------
/src/components/dictionaries/etymonline/config.ts:
--------------------------------------------------------------------------------
1 | import { DictItem } from '@/app-config/dicts'
2 |
3 | export type EtymonlineConfig = DictItem<{
4 | resultnum: number
5 | chart: boolean
6 | }>
7 |
8 | export default (): EtymonlineConfig => ({
9 | lang: '10000000',
10 | selectionLang: {
11 | english: true,
12 | chinese: false,
13 | japanese: false,
14 | korean: false,
15 | french: false,
16 | spanish: false,
17 | deutsch: false,
18 | others: false,
19 | matchAll: false
20 | },
21 | defaultUnfold: {
22 | english: true,
23 | chinese: true,
24 | japanese: true,
25 | korean: true,
26 | french: true,
27 | spanish: true,
28 | deutsch: true,
29 | others: true,
30 | matchAll: false
31 | },
32 | preferredHeight: 265,
33 | selectionWC: {
34 | min: 1,
35 | max: 5
36 | },
37 | options: {
38 | resultnum: 4,
39 | chart: true
40 | }
41 | })
42 |
--------------------------------------------------------------------------------
/src/components/dictionaries/etymonline/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/components/dictionaries/etymonline/favicon.png
--------------------------------------------------------------------------------
/src/components/dictionaries/eudic/View.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react'
2 | import { EudicResult } from './engine'
3 | import Speaker from '@/components/Speaker'
4 | import { ViewPorps } from '@/components/dictionaries/helpers'
5 |
6 | export const DictEudic: FC> = ({ result }) => (
7 |
8 | {result.map(item => (
9 | -
10 |
11 | {item.eng}
12 |
13 | {item.chs}
14 |
17 |
18 | ))}
19 |
20 | )
21 |
22 | export default DictEudic
23 |
--------------------------------------------------------------------------------
/src/components/dictionaries/eudic/_locales.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": {
3 | "en": "Eudic",
4 | "zh-CN": "双语例句",
5 | "zh-TW": "雙語例句"
6 | },
7 | "options": {
8 | "resultnum": {
9 | "en": "Show",
10 | "zh-CN": "结果数量",
11 | "zh-TW": "結果數量"
12 | },
13 | "resultnum_unit": {
14 | "en": "results",
15 | "zh-CN": "个",
16 | "zh-TW": "個"
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/components/dictionaries/eudic/_style.shadow.scss:
--------------------------------------------------------------------------------
1 | .dictEudic-Item {
2 | margin-bottom: 10px;
3 |
4 | p {
5 | margin: 0;
6 | }
7 | }
8 |
9 | .dictEudic-Channel {
10 | color: #999;
11 |
12 | &::before {
13 | content: '《';
14 | }
15 |
16 | &::after {
17 | content: '》';
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/components/dictionaries/eudic/config.ts:
--------------------------------------------------------------------------------
1 | import { DictItem } from '@/app-config/dicts'
2 |
3 | export type EudicConfig = DictItem<{
4 | resultnum: number
5 | }>
6 |
7 | export default (): EudicConfig => ({
8 | lang: '11000000',
9 | selectionLang: {
10 | english: true,
11 | chinese: true,
12 | japanese: false,
13 | korean: false,
14 | french: false,
15 | spanish: false,
16 | deutsch: false,
17 | others: false,
18 | matchAll: false
19 | },
20 | defaultUnfold: {
21 | english: true,
22 | chinese: true,
23 | japanese: true,
24 | korean: true,
25 | french: true,
26 | spanish: true,
27 | deutsch: true,
28 | others: true,
29 | matchAll: false
30 | },
31 | preferredHeight: 240,
32 | selectionWC: {
33 | min: 1,
34 | max: 5
35 | },
36 | options: {
37 | resultnum: 10
38 | }
39 | })
40 |
--------------------------------------------------------------------------------
/src/components/dictionaries/eudic/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/components/dictionaries/eudic/favicon.png
--------------------------------------------------------------------------------
/src/components/dictionaries/google/View.tsx:
--------------------------------------------------------------------------------
1 | export { MachineTrans as default } from '@/components/MachineTrans/MachineTrans'
2 |
--------------------------------------------------------------------------------
/src/components/dictionaries/google/_locales.ts:
--------------------------------------------------------------------------------
1 | import { getMachineLocales } from '../locales'
2 |
3 | export const locales = getMachineLocales(
4 | {
5 | en: 'Google Translation',
6 | 'zh-CN': '谷歌翻译',
7 | 'zh-TW': '谷歌翻譯'
8 | },
9 | {
10 | cnfirst: {
11 | en: 'Use google.cn first',
12 | 'zh-CN': '优先使用 google.cn',
13 | 'zh-TW': '優先使用 google.cn'
14 | },
15 | concurrent: {
16 | en: 'Search concurrently',
17 | 'zh-CN': '并行查询',
18 | 'zh-TW': '並行查詢'
19 | }
20 | },
21 | {
22 | concurrent: {
23 | en: 'Search .com and .cn concurrently',
24 | 'zh-CN': '同时搜索 .com 和 .cn 取最先返回的结果',
25 | 'zh-TW': '同時搜尋 .com 和 .cn 取最先返回的結果'
26 | }
27 | }
28 | )
29 |
--------------------------------------------------------------------------------
/src/components/dictionaries/google/_style.shadow.scss:
--------------------------------------------------------------------------------
1 | @import '@/components/MachineTrans/MachineTrans.scss';
2 |
--------------------------------------------------------------------------------
/src/components/dictionaries/google/config.ts:
--------------------------------------------------------------------------------
1 | import {
2 | MachineDictItem,
3 | machineConfig
4 | } from '@/components/MachineTrans/engine'
5 | import { Language } from '@opentranslate/translator'
6 | import { Subunion } from '@/typings/helpers'
7 |
8 | export type GoogleLanguage = Subunion<
9 | Language,
10 | 'zh-CN' | 'zh-TW' | 'en' | 'ja' | 'ko' | 'fr' | 'de' | 'es' | 'ru' | 'nl'
11 | >
12 |
13 | export type GoogleConfig = MachineDictItem<
14 | GoogleLanguage,
15 | {
16 | concurrent: boolean
17 | }
18 | >
19 |
20 | export default (): GoogleConfig =>
21 | machineConfig(
22 | ['zh-CN', 'zh-TW', 'en', 'ja', 'ko', 'fr', 'de', 'es', 'ru', 'nl'],
23 | {},
24 | {
25 | concurrent: false
26 | },
27 | {}
28 | )
29 |
--------------------------------------------------------------------------------
/src/components/dictionaries/google/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/components/dictionaries/google/favicon.png
--------------------------------------------------------------------------------
/src/components/dictionaries/googledict/_locales.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": {
3 | "en": "Google Dictionary",
4 | "zh-CN": "谷歌词典",
5 | "zh-TW": "谷歌詞典"
6 | },
7 | "options": {
8 | "enresult": {
9 | "en": "English Result",
10 | "zh-CN": "强制显示英文结果",
11 | "zh-TW": "強制顯示英文結果"
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/components/dictionaries/googledict/config.ts:
--------------------------------------------------------------------------------
1 | import { DictItem } from '@/app-config/dicts'
2 |
3 | export type GoogledictConfig = DictItem<{
4 | enresult: boolean
5 | }>
6 |
7 | export default (): GoogledictConfig => ({
8 | lang: '11110000',
9 | selectionLang: {
10 | english: true,
11 | chinese: true,
12 | japanese: true,
13 | korean: true,
14 | french: true,
15 | spanish: true,
16 | deutsch: true,
17 | others: true,
18 | matchAll: false
19 | },
20 | defaultUnfold: {
21 | english: true,
22 | chinese: true,
23 | japanese: true,
24 | korean: true,
25 | french: true,
26 | spanish: true,
27 | deutsch: true,
28 | others: true,
29 | matchAll: false
30 | },
31 | preferredHeight: 240,
32 | selectionWC: {
33 | min: 1,
34 | max: 5
35 | },
36 | options: {
37 | enresult: true
38 | }
39 | })
40 |
--------------------------------------------------------------------------------
/src/components/dictionaries/googledict/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/components/dictionaries/googledict/favicon.png
--------------------------------------------------------------------------------
/src/components/dictionaries/guoyu/_locales.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": {
3 | "en": "國語辭典",
4 | "zh-CN": "国语辞典",
5 | "zh-TW": "國語辭典"
6 | },
7 | "options": {
8 | "trans": {
9 | "en": "Show Translation",
10 | "zh-CN": "显示翻译",
11 | "zh-TW": "顯示翻譯"
12 | }
13 | },
14 | "helps": {
15 | "trans": {
16 | "en": "Show translation of other languages.",
17 | "zh-CN": "显示其它语言的翻译。",
18 | "zh-TW": "顯示其它語言的翻譯。"
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/components/dictionaries/guoyu/_style.shadow.scss:
--------------------------------------------------------------------------------
1 | .dictMoe-H {
2 | margin-bottom: 10px;
3 | }
4 |
5 | .dictMoe-Title {
6 | display: inline;
7 | font-size: 1.6em;
8 | font-weight: normal;
9 | margin: 0 0.2em;
10 | }
11 |
12 | .dictMoe-Defs {
13 | margin: 0 0 10px;
14 | padding-left: 1.5em;
15 | }
16 |
17 | .dictMoe-Defs_F {
18 | margin: 0;
19 | }
20 |
21 | .dictMoe-Defs_E {
22 | margin: 0;
23 | color: var(--color-font-grey);
24 | }
25 |
26 | .dictMoe-Trans {
27 | display: table;
28 | }
29 |
30 | .dictMoe-Trans_Pos {
31 | display: table-cell;
32 | width: 2em;
33 | font-weight: bold;
34 | text-align: right;
35 | }
36 |
37 | .dictMoe-Trans_Def {
38 | display: table-cell;
39 | padding: 0 12px;
40 | }
41 |
42 | .dictMoe-Link {
43 | &:link,
44 | &:visited,
45 | &:hover,
46 | &:active {
47 | color: inherit;
48 | text-decoration: none;
49 | }
50 |
51 | &:focus,
52 | &:hover {
53 | background: #16a085;
54 | outline: 3px solid #16a085;
55 | color: #fff;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/components/dictionaries/guoyu/config.ts:
--------------------------------------------------------------------------------
1 | import { DictItem } from '@/app-config/dicts'
2 |
3 | export type GuoyuConfig = DictItem<{
4 | /** show translation */
5 | trans: boolean
6 | }>
7 |
8 | export default (): GuoyuConfig => ({
9 | lang: '00100000',
10 | selectionLang: {
11 | english: false,
12 | chinese: true,
13 | japanese: false,
14 | korean: false,
15 | french: false,
16 | spanish: false,
17 | deutsch: false,
18 | others: false,
19 | matchAll: false
20 | },
21 | defaultUnfold: {
22 | english: true,
23 | chinese: true,
24 | japanese: true,
25 | korean: true,
26 | french: true,
27 | spanish: true,
28 | deutsch: true,
29 | others: true,
30 | matchAll: false
31 | },
32 | preferredHeight: 265,
33 | selectionWC: {
34 | min: 1,
35 | max: 5
36 | },
37 | options: {
38 | trans: true
39 | }
40 | })
41 |
--------------------------------------------------------------------------------
/src/components/dictionaries/guoyu/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/components/dictionaries/guoyu/favicon.png
--------------------------------------------------------------------------------
/src/components/dictionaries/hjdict/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/components/dictionaries/hjdict/favicon.png
--------------------------------------------------------------------------------
/src/components/dictionaries/jikipedia/_locales.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": {
3 | "en": "Jikipedia",
4 | "zh-CN": "小鸡词典",
5 | "zh-TW": "小雞詞典"
6 | },
7 | "options": {
8 | "resultnum": {
9 | "en": "Show",
10 | "zh-CN": "结果数量",
11 | "zh-TW": "結果數量"
12 | },
13 | "resultnum_unit": {
14 | "en": "results",
15 | "zh-CN": "个",
16 | "zh-TW": "個"
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/components/dictionaries/jikipedia/config.ts:
--------------------------------------------------------------------------------
1 | import { DictItem } from '@/app-config/dicts'
2 |
3 | export type UrbanConfig = DictItem<{
4 | resultnum: number
5 | }>
6 |
7 | export default (): UrbanConfig => ({
8 | lang: '01000000',
9 | selectionLang: {
10 | english: true,
11 | chinese: true,
12 | japanese: false,
13 | korean: false,
14 | french: false,
15 | spanish: false,
16 | deutsch: false,
17 | others: true,
18 | matchAll: false
19 | },
20 | defaultUnfold: {
21 | english: true,
22 | chinese: true,
23 | japanese: true,
24 | korean: true,
25 | french: true,
26 | spanish: true,
27 | deutsch: true,
28 | others: true,
29 | matchAll: false
30 | },
31 | preferredHeight: 380,
32 | selectionWC: {
33 | min: 1,
34 | max: 5
35 | },
36 | options: {
37 | resultnum: 4
38 | }
39 | })
40 |
--------------------------------------------------------------------------------
/src/components/dictionaries/jikipedia/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/components/dictionaries/jikipedia/favicon.png
--------------------------------------------------------------------------------
/src/components/dictionaries/jukuu/_locales.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": {
3 | "en": "Jukuu",
4 | "zh-CN": "句酷",
5 | "zh-TW": "句酷"
6 | },
7 | "options": {
8 | "lang": {
9 | "en": "Language",
10 | "zh-CN": "语言",
11 | "zh-TW": "語言"
12 | },
13 | "lang-zheng": {
14 | "en": "Chinese-English",
15 | "zh-CN": "中英",
16 | "zh-TW": "中英"
17 | },
18 | "lang-engjp": {
19 | "en": "English-Japanese",
20 | "zh-CN": "英日",
21 | "zh-TW": "英日"
22 | },
23 | "lang-zhjp": {
24 | "en": "Chinese-Japanese",
25 | "zh-CN": "中日",
26 | "zh-TW": "中日"
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/components/dictionaries/jukuu/_style.shadow.scss:
--------------------------------------------------------------------------------
1 | .dictJukuu-Sens {
2 | padding-left: 20px;
3 |
4 | b {
5 | color: #f9690e;
6 | }
7 |
8 | p {
9 | margin: 0;
10 | }
11 | }
12 |
13 | .dictJukuu-Sen {
14 | list-style-type: disc;
15 | margin: 0.5em 0;
16 | }
17 |
18 | .dictJukuu-Ori {
19 | color: olive;
20 | }
21 |
22 | .dictJukuu-Src {
23 | font-size: 0.9em;
24 | text-align: right;
25 | color: #777;
26 | }
27 |
--------------------------------------------------------------------------------
/src/components/dictionaries/jukuu/config.ts:
--------------------------------------------------------------------------------
1 | import { DictItem } from '@/app-config/dicts'
2 |
3 | export type JukuuConfig = DictItem<{
4 | lang: 'zheng' | 'engjp' | 'zhjp'
5 | }>
6 |
7 | export default (): JukuuConfig => ({
8 | lang: '11010000',
9 | selectionLang: {
10 | english: true,
11 | chinese: true,
12 | japanese: true,
13 | korean: true,
14 | french: true,
15 | spanish: true,
16 | deutsch: true,
17 | others: false,
18 | matchAll: false
19 | },
20 | defaultUnfold: {
21 | english: true,
22 | chinese: true,
23 | japanese: true,
24 | korean: true,
25 | french: true,
26 | spanish: true,
27 | deutsch: true,
28 | others: true,
29 | matchAll: false
30 | },
31 | preferredHeight: 300,
32 | selectionWC: {
33 | min: 1,
34 | max: 99999
35 | },
36 | options: {
37 | lang: 'zheng'
38 | },
39 | options_sel: {
40 | lang: ['zheng', 'engjp', 'zhjp']
41 | }
42 | })
43 |
--------------------------------------------------------------------------------
/src/components/dictionaries/jukuu/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/components/dictionaries/jukuu/favicon.png
--------------------------------------------------------------------------------
/src/components/dictionaries/lexico/_locales.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": {
3 | "en": "Lexico",
4 | "zh-CN": "Lexico",
5 | "zh-TW": "Lexico"
6 | },
7 | "options": {
8 | "related": {
9 | "en": "Show related results",
10 | "zh-CN": "失败时显示备选",
11 | "zh-TW": "失敗時顯示備選"
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/components/dictionaries/lexico/config.ts:
--------------------------------------------------------------------------------
1 | import { DictItem } from '@/app-config/dicts'
2 |
3 | export type LexicoConfig = DictItem<{
4 | related: boolean
5 | }>
6 |
7 | export default (): LexicoConfig => ({
8 | lang: '10000000',
9 | selectionLang: {
10 | english: true,
11 | chinese: false,
12 | japanese: false,
13 | korean: false,
14 | french: false,
15 | spanish: false,
16 | deutsch: false,
17 | others: false,
18 | matchAll: false
19 | },
20 | defaultUnfold: {
21 | english: true,
22 | chinese: true,
23 | japanese: true,
24 | korean: true,
25 | french: true,
26 | spanish: true,
27 | deutsch: true,
28 | others: true,
29 | matchAll: false
30 | },
31 | preferredHeight: 265,
32 | selectionWC: {
33 | min: 1,
34 | max: 5
35 | },
36 | options: {
37 | related: true
38 | }
39 | })
40 |
--------------------------------------------------------------------------------
/src/components/dictionaries/lexico/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/components/dictionaries/lexico/favicon.png
--------------------------------------------------------------------------------
/src/components/dictionaries/liangan/View.tsx:
--------------------------------------------------------------------------------
1 | import { DictGuoyu } from '../guoyu/View'
2 |
3 | export const DictLiangAn = DictGuoyu
4 |
5 | DictLiangAn.displayName = 'LiangAn'
6 |
7 | export default DictLiangAn
8 |
--------------------------------------------------------------------------------
/src/components/dictionaries/liangan/_locales.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": {
3 | "en": "兩岸詞典",
4 | "zh-CN": "两岸词典",
5 | "zh-TW": "兩岸詞典"
6 | },
7 | "options": {
8 | "trans": {
9 | "en": "Show Translation",
10 | "zh-CN": "显示翻译",
11 | "zh-TW": "顯示翻譯"
12 | }
13 | },
14 | "helps": {
15 | "trans": {
16 | "en": "Show translation of other languages.",
17 | "zh-CN": "显示其它语言的翻译。",
18 | "zh-TW": "顯示其它語言的翻譯。"
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/components/dictionaries/liangan/_style.shadow.scss:
--------------------------------------------------------------------------------
1 | @import '../guoyu/_style.shadow.scss';
2 |
--------------------------------------------------------------------------------
/src/components/dictionaries/liangan/config.ts:
--------------------------------------------------------------------------------
1 | import { DictItem } from '@/app-config/dicts'
2 |
3 | export type LianganConfig = DictItem<{
4 | trans: boolean
5 | }>
6 |
7 | export default (): LianganConfig => ({
8 | lang: '00100000',
9 | selectionLang: {
10 | english: false,
11 | chinese: true,
12 | japanese: false,
13 | korean: false,
14 | french: false,
15 | spanish: false,
16 | deutsch: false,
17 | others: false,
18 | matchAll: false
19 | },
20 | defaultUnfold: {
21 | english: true,
22 | chinese: true,
23 | japanese: true,
24 | korean: true,
25 | french: true,
26 | spanish: true,
27 | deutsch: true,
28 | others: true,
29 | matchAll: false
30 | },
31 | preferredHeight: 265,
32 | selectionWC: {
33 | min: 1,
34 | max: 5
35 | },
36 | options: {
37 | trans: false
38 | }
39 | })
40 |
--------------------------------------------------------------------------------
/src/components/dictionaries/liangan/engine.ts:
--------------------------------------------------------------------------------
1 | import { SearchFunction, GetSrcPageFunction, getChsToChz } from '../helpers'
2 | import { moedictSearch, GuoYuResult } from '../guoyu/engine'
3 |
4 | export const getSrcPage: GetSrcPageFunction = async text => {
5 | const chsToChz = await getChsToChz()
6 | return `https://www.moedict.tw/~${chsToChz(text)}`
7 | }
8 |
9 | export type LiangAnResult = GuoYuResult
10 |
11 | export const search: SearchFunction = (
12 | text,
13 | config,
14 | profile,
15 | payload
16 | ) => {
17 | return moedictSearch(
18 | 'c',
19 | text,
20 | config,
21 | profile.dicts.all.liangan.options
22 | ).then(result => {
23 | if (result.result.h) {
24 | result.result.h.forEach(h => {
25 | if (h.p) {
26 | h.p = h.p.replace('
陸⃝', ' [大陆]: ')
27 | }
28 | })
29 | }
30 | return result
31 | })
32 | }
33 |
--------------------------------------------------------------------------------
/src/components/dictionaries/liangan/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/components/dictionaries/liangan/favicon.png
--------------------------------------------------------------------------------
/src/components/dictionaries/longman/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/components/dictionaries/longman/favicon.png
--------------------------------------------------------------------------------
/src/components/dictionaries/macmillan/_locales.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": {
3 | "en": "Macmillan",
4 | "zh-CN": "麦克米伦",
5 | "zh-TW": "麥克米倫"
6 | },
7 | "options": {
8 | "related": {
9 | "en": "Show related results",
10 | "zh-CN": "失败时显示备选",
11 | "zh-TW": "失敗時顯示備選"
12 | },
13 | "locale": {
14 | "en": "Locale",
15 | "zh-CN": "方言",
16 | "zh-TW": "方言"
17 | },
18 | "locale-uk": {
19 | "en": "UK",
20 | "zh-CN": "英式",
21 | "zh-TW": "英式"
22 | },
23 | "locale-us": {
24 | "en": "US",
25 | "zh-CN": "美式",
26 | "zh-TW": "美式"
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/components/dictionaries/macmillan/config.ts:
--------------------------------------------------------------------------------
1 | import { DictItem } from '@/app-config/dicts'
2 |
3 | export type MacmillanConfig = DictItem<{
4 | related: boolean
5 | locale: 'uk' | 'us'
6 | }>
7 |
8 | export default (): MacmillanConfig => ({
9 | lang: '10000000',
10 | selectionLang: {
11 | english: true,
12 | chinese: false,
13 | japanese: false,
14 | korean: false,
15 | french: false,
16 | spanish: false,
17 | deutsch: false,
18 | others: false,
19 | matchAll: false
20 | },
21 | defaultUnfold: {
22 | english: true,
23 | chinese: true,
24 | japanese: true,
25 | korean: true,
26 | french: true,
27 | spanish: true,
28 | deutsch: true,
29 | others: true,
30 | matchAll: false
31 | },
32 | preferredHeight: 465,
33 | selectionWC: {
34 | min: 1,
35 | max: 5
36 | },
37 | options: {
38 | related: true,
39 | locale: 'uk'
40 | },
41 | options_sel: {
42 | locale: ['uk', 'us']
43 | }
44 | })
45 |
--------------------------------------------------------------------------------
/src/components/dictionaries/macmillan/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/components/dictionaries/macmillan/favicon.png
--------------------------------------------------------------------------------
/src/components/dictionaries/merriamwebster/_locales.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": {
3 | "en": "Merriam-Webster's Dictionary",
4 | "zh-CN": "韦氏词典",
5 | "zh-TW": "韋氏字典"
6 | },
7 | "options": {
8 | "resultnum": {
9 | "en": "Show",
10 | "zh-CN": "结果数量",
11 | "zh-TW": "結果數量"
12 | },
13 | "resultnum_unit": {
14 | "en": "results",
15 | "zh-CN": "个",
16 | "zh-TW": "個"
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/components/dictionaries/merriamwebster/config.ts:
--------------------------------------------------------------------------------
1 | import { DictItem } from '@/app-config/dicts'
2 |
3 | export type MerriamWebsterConfig = DictItem<{
4 | resultnum: number
5 | }>
6 |
7 | export default (): MerriamWebsterConfig => ({
8 | lang: '10000000',
9 | selectionLang: {
10 | english: true,
11 | chinese: false,
12 | japanese: false,
13 | korean: false,
14 | french: false,
15 | spanish: false,
16 | deutsch: false,
17 | others: false,
18 | matchAll: false
19 | },
20 | defaultUnfold: {
21 | english: true,
22 | chinese: true,
23 | japanese: true,
24 | korean: true,
25 | french: true,
26 | spanish: true,
27 | deutsch: true,
28 | others: true,
29 | matchAll: false
30 | },
31 | preferredHeight: 180,
32 | selectionWC: {
33 | min: 1,
34 | max: 5
35 | },
36 | options: {
37 | resultnum: 4
38 | }
39 | })
40 |
--------------------------------------------------------------------------------
/src/components/dictionaries/merriamwebster/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/components/dictionaries/merriamwebster/favicon.png
--------------------------------------------------------------------------------
/src/components/dictionaries/mojidict/_locales.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": {
3 | "en": "MOJi辞書",
4 | "zh-CN": "MOJi辞書",
5 | "zh-TW": "MOJi辞書"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/components/dictionaries/mojidict/_style.shadow.scss:
--------------------------------------------------------------------------------
1 | .dictMojidict-Word_Title {
2 | margin-bottom: 0;
3 | }
4 |
5 | .dictMojidict-Word_Trans {
6 | margin-top: 0;
7 | padding-left: 0.5em;
8 | font-size: 0.9em;
9 | color: #999;
10 | }
11 |
12 | .dictMojidict-List {
13 | padding-left: 1em;
14 | }
15 |
16 | .dictMojidict-Sublist {
17 | padding-left: 0.5em;
18 | }
19 |
20 | .dictMojidict-ListItem_Disc {
21 | list-style-type: disc;
22 | }
23 |
--------------------------------------------------------------------------------
/src/components/dictionaries/mojidict/config.ts:
--------------------------------------------------------------------------------
1 | import { DictItem } from '@/app-config/dicts'
2 |
3 | export type LianganConfig = DictItem
4 |
5 | export default (): LianganConfig => ({
6 | lang: '10010000',
7 | selectionLang: {
8 | english: false,
9 | chinese: true,
10 | japanese: true,
11 | korean: false,
12 | french: false,
13 | spanish: false,
14 | deutsch: false,
15 | others: false,
16 | matchAll: false
17 | },
18 | defaultUnfold: {
19 | english: true,
20 | chinese: true,
21 | japanese: true,
22 | korean: true,
23 | french: true,
24 | spanish: true,
25 | deutsch: true,
26 | others: true,
27 | matchAll: false
28 | },
29 | preferredHeight: 265,
30 | selectionWC: {
31 | min: 1,
32 | max: 5
33 | }
34 | })
35 |
--------------------------------------------------------------------------------
/src/components/dictionaries/mojidict/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/components/dictionaries/mojidict/favicon.png
--------------------------------------------------------------------------------
/src/components/dictionaries/naver/_locales.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": {
3 | "en": "Naver韩国语词典",
4 | "zh-CN": "Naver韩国语词典",
5 | "zh-TW": "Naver韩国语词典"
6 | },
7 | "options": {
8 | "hanAsJa": {
9 | "en": "Search Chinese with Japanese Dictionary",
10 | "zh-CN": "用日语词典查中文",
11 | "zh-TW": "用日語詞典查漢字"
12 | },
13 | "korAsJa": {
14 | "en": "Search Korean with Japanese Dictionary",
15 | "zh-CN": "用日语词典查韩语",
16 | "zh-TW": "用日语词典查韓語"
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/components/dictionaries/naver/config.ts:
--------------------------------------------------------------------------------
1 | import { DictItem } from '@/app-config/dicts'
2 |
3 | export type NaverConfig = DictItem<{
4 | hanAsJa: boolean
5 | korAsJa: boolean
6 | }>
7 |
8 | export default (): NaverConfig => ({
9 | lang: '01011000',
10 | selectionLang: {
11 | english: false,
12 | chinese: true,
13 | japanese: true,
14 | korean: true,
15 | french: false,
16 | spanish: false,
17 | deutsch: false,
18 | others: false,
19 | matchAll: false
20 | },
21 | defaultUnfold: {
22 | english: true,
23 | chinese: true,
24 | japanese: true,
25 | korean: true,
26 | french: true,
27 | spanish: true,
28 | deutsch: true,
29 | others: true,
30 | matchAll: false
31 | },
32 | preferredHeight: 465,
33 | selectionWC: {
34 | min: 1,
35 | max: 10
36 | },
37 | options: {
38 | hanAsJa: false,
39 | korAsJa: false
40 | }
41 | })
42 |
--------------------------------------------------------------------------------
/src/components/dictionaries/naver/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/components/dictionaries/naver/favicon.png
--------------------------------------------------------------------------------
/src/components/dictionaries/oaldict/_locales.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": {
3 | "en": "Oxford Learner's Dict",
4 | "zh-CN": "牛津高阶词典",
5 | "zh-TW": "牛津高階詞典"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/components/dictionaries/oaldict/config.ts:
--------------------------------------------------------------------------------
1 | import { DictItem } from '@/app-config/dicts'
2 |
3 | export type OalDictConfig = DictItem
4 |
5 | export default (): OalDictConfig => ({
6 | lang: '10000000',
7 | selectionLang: {
8 | english: true,
9 | chinese: false,
10 | japanese: false,
11 | korean: false,
12 | french: false,
13 | spanish: false,
14 | deutsch: false,
15 | others: false,
16 | matchAll: false
17 | },
18 | defaultUnfold: {
19 | english: true,
20 | chinese: true,
21 | japanese: true,
22 | korean: true,
23 | french: true,
24 | spanish: true,
25 | deutsch: true,
26 | others: true,
27 | matchAll: false
28 | },
29 | preferredHeight: 240,
30 | selectionWC: {
31 | min: 1,
32 | max: 5
33 | }
34 | })
35 |
--------------------------------------------------------------------------------
/src/components/dictionaries/oaldict/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/components/dictionaries/oaldict/favicon.png
--------------------------------------------------------------------------------
/src/components/dictionaries/renren/_locales.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": {
3 | "en": "91dict",
4 | "zh-CN": "人人词典",
5 | "zh-TW": "人人詞典"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/components/dictionaries/renren/config.ts:
--------------------------------------------------------------------------------
1 | import { DictItem } from '@/app-config/dicts'
2 |
3 | export type RenrenConfig = DictItem
4 |
5 | export default (): RenrenConfig => ({
6 | lang: '11000000',
7 | selectionLang: {
8 | english: true,
9 | chinese: true,
10 | japanese: false,
11 | korean: false,
12 | french: false,
13 | spanish: false,
14 | deutsch: false,
15 | others: false,
16 | matchAll: false
17 | },
18 | defaultUnfold: {
19 | english: true,
20 | chinese: true,
21 | japanese: true,
22 | korean: true,
23 | french: true,
24 | spanish: true,
25 | deutsch: true,
26 | others: true,
27 | matchAll: false
28 | },
29 | preferredHeight: 400,
30 | selectionWC: {
31 | min: 1,
32 | max: 999
33 | }
34 | })
35 |
--------------------------------------------------------------------------------
/src/components/dictionaries/renren/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/components/dictionaries/renren/favicon.png
--------------------------------------------------------------------------------
/src/components/dictionaries/shanbay/_locales.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": {
3 | "en": "Shanbay Dictionary",
4 | "zh-CN": "扇贝词典",
5 | "zh-TW": "扇貝詞典"
6 | },
7 | "options": {
8 | "basic": {
9 | "en": "Show basic meaning",
10 | "zh-CN": "显示简单释义",
11 | "zh-TW": "顯示簡單解釋"
12 | },
13 | "sentence": {
14 | "en": "Show sentences",
15 | "zh-CN": "显示权威例句",
16 | "zh-TW": "顯示權威例句"
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/components/dictionaries/shanbay/_style.shadow.scss:
--------------------------------------------------------------------------------
1 | .dictShanbay-HeaderContainer {
2 | display: flex;
3 | align-items: center;
4 | flex-wrap: wrap;
5 | }
6 |
7 | .dictShanbay-Title {
8 | font-size: 1.5em;
9 | margin-right: 8px;
10 | }
11 |
12 | .dictShanbay-Pattern {
13 | margin-top: 0.5em;
14 | }
15 |
16 | .dictShanbay-SecTitle {
17 | font-size: 1.1em;
18 | margin-top: 1em;
19 | border-bottom: 1px solid rgba(199, 110, 6, 0.5);
20 | }
21 |
22 | .dictShanbay-Basic {
23 | margin: 0.5em 0;
24 |
25 | .definition-pos {
26 | padding-right: 0.5em;
27 | }
28 | }
29 |
30 | .dictShanbay-Sentence {
31 | margin: 0;
32 | padding: 0 0 0 1.5em;
33 | }
34 |
--------------------------------------------------------------------------------
/src/components/dictionaries/shanbay/config.ts:
--------------------------------------------------------------------------------
1 | import { DictItem } from '@/app-config/dicts'
2 |
3 | export type ShanbayConfig = DictItem<{
4 | basic: boolean
5 | sentence: boolean
6 | }>
7 |
8 | export default (): ShanbayConfig => ({
9 | lang: '10000000',
10 | selectionLang: {
11 | english: true,
12 | chinese: false,
13 | japanese: false,
14 | korean: false,
15 | french: false,
16 | spanish: false,
17 | deutsch: false,
18 | others: false,
19 | matchAll: false
20 | },
21 | defaultUnfold: {
22 | english: true,
23 | chinese: true,
24 | japanese: true,
25 | korean: true,
26 | french: true,
27 | spanish: true,
28 | deutsch: true,
29 | others: true,
30 | matchAll: false
31 | },
32 | preferredHeight: 150,
33 | selectionWC: {
34 | min: 1,
35 | max: 30
36 | },
37 | options: {
38 | basic: true,
39 | sentence: true
40 | }
41 | })
42 |
--------------------------------------------------------------------------------
/src/components/dictionaries/shanbay/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/components/dictionaries/shanbay/favicon.png
--------------------------------------------------------------------------------
/src/components/dictionaries/sogou/View.tsx:
--------------------------------------------------------------------------------
1 | export { MachineTrans as default } from '@/components/MachineTrans/MachineTrans'
2 |
--------------------------------------------------------------------------------
/src/components/dictionaries/sogou/_locales.ts:
--------------------------------------------------------------------------------
1 | import { getMachineLocales } from '../locales'
2 |
3 | export const locales = getMachineLocales({
4 | en: 'Sogou Translation',
5 | 'zh-CN': '搜狗翻译',
6 | 'zh-TW': '搜狗翻譯'
7 | })
8 |
--------------------------------------------------------------------------------
/src/components/dictionaries/sogou/_style.shadow.scss:
--------------------------------------------------------------------------------
1 | @import '@/components/MachineTrans/MachineTrans.scss';
2 |
--------------------------------------------------------------------------------
/src/components/dictionaries/sogou/auth.ts:
--------------------------------------------------------------------------------
1 | export const auth = {
2 | pid: '',
3 | key: ''
4 | }
5 |
6 | export const url = 'https://deepi.sogou.com/?from=translatepc'
7 |
--------------------------------------------------------------------------------
/src/components/dictionaries/sogou/config.ts:
--------------------------------------------------------------------------------
1 | import {
2 | MachineDictItem,
3 | machineConfig
4 | } from '@/components/MachineTrans/engine'
5 | import { Language } from '@opentranslate/translator'
6 | import { Subunion } from '@/typings/helpers'
7 |
8 | export type SogouLanguage = Subunion<
9 | Language,
10 | 'zh-CN' | 'zh-TW' | 'en' | 'ja' | 'ko' | 'fr' | 'de' | 'es' | 'ru'
11 | >
12 |
13 | export type SogouConfig = MachineDictItem
14 |
15 | export default (): SogouConfig =>
16 | machineConfig(
17 | ['zh-CN', 'zh-TW', 'en', 'ja', 'ko', 'fr', 'de', 'es', 'ru'],
18 | {},
19 | {},
20 | {}
21 | )
22 |
--------------------------------------------------------------------------------
/src/components/dictionaries/sogou/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/components/dictionaries/sogou/favicon.png
--------------------------------------------------------------------------------
/src/components/dictionaries/tencent/View.tsx:
--------------------------------------------------------------------------------
1 | export { MachineTrans as default } from '@/components/MachineTrans/MachineTrans'
2 |
--------------------------------------------------------------------------------
/src/components/dictionaries/tencent/_locales.ts:
--------------------------------------------------------------------------------
1 | import { getMachineLocales } from '../locales'
2 |
3 | export const locales = getMachineLocales({
4 | en: 'Tencent Translate',
5 | 'zh-CN': '腾讯翻译君',
6 | 'zh-TW': '騰訊翻譯君'
7 | })
8 |
--------------------------------------------------------------------------------
/src/components/dictionaries/tencent/_style.shadow.scss:
--------------------------------------------------------------------------------
1 | @import '@/components/MachineTrans/MachineTrans.scss';
2 |
--------------------------------------------------------------------------------
/src/components/dictionaries/tencent/auth.ts:
--------------------------------------------------------------------------------
1 | export const auth = {
2 | secretId: '',
3 | secretKey: ''
4 | }
5 |
6 | export const url = 'https://curl.qcloud.com/imsowZzT'
7 |
--------------------------------------------------------------------------------
/src/components/dictionaries/tencent/config.ts:
--------------------------------------------------------------------------------
1 | import {
2 | MachineDictItem,
3 | machineConfig
4 | } from '@/components/MachineTrans/engine'
5 | import { Language } from '@opentranslate/translator'
6 | import { Subunion } from '@/typings/helpers'
7 |
8 | export type TencentLanguage = Subunion<
9 | Language,
10 | 'zh-CN' | 'en' | 'ja' | 'ko' | 'fr' | 'de' | 'es' | 'ru'
11 | >
12 |
13 | export type TencentConfig = MachineDictItem
14 |
15 | export default (): TencentConfig =>
16 | machineConfig(
17 | ['zh-CN', 'en', 'ja', 'ko', 'fr', 'de', 'es', 'ru'],
18 | {
19 | lang: '11011111'
20 | },
21 | {},
22 | {}
23 | )
24 |
--------------------------------------------------------------------------------
/src/components/dictionaries/tencent/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/components/dictionaries/tencent/favicon.png
--------------------------------------------------------------------------------
/src/components/dictionaries/urban/_locales.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": {
3 | "en": "Urban",
4 | "zh-CN": "Urban",
5 | "zh-TW": "Urban"
6 | },
7 | "options": {
8 | "resultnum": {
9 | "en": "Show",
10 | "zh-CN": "结果数量",
11 | "zh-TW": "結果數量"
12 | },
13 | "resultnum_unit": {
14 | "en": "results",
15 | "zh-CN": "个",
16 | "zh-TW": "個"
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/components/dictionaries/urban/config.ts:
--------------------------------------------------------------------------------
1 | import { DictItem } from '@/app-config/dicts'
2 |
3 | export type UrbanConfig = DictItem<{
4 | resultnum: number
5 | }>
6 |
7 | export default (): UrbanConfig => ({
8 | lang: '10000000',
9 | selectionLang: {
10 | english: true,
11 | chinese: false,
12 | japanese: false,
13 | korean: false,
14 | french: false,
15 | spanish: false,
16 | deutsch: false,
17 | others: false,
18 | matchAll: false
19 | },
20 | defaultUnfold: {
21 | english: true,
22 | chinese: true,
23 | japanese: true,
24 | korean: true,
25 | french: true,
26 | spanish: true,
27 | deutsch: true,
28 | others: true,
29 | matchAll: false
30 | },
31 | preferredHeight: 180,
32 | selectionWC: {
33 | min: 1,
34 | max: 5
35 | },
36 | options: {
37 | resultnum: 4
38 | }
39 | })
40 |
--------------------------------------------------------------------------------
/src/components/dictionaries/urban/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/components/dictionaries/urban/favicon.png
--------------------------------------------------------------------------------
/src/components/dictionaries/vocabulary/View.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react'
2 | import { VocabularyResult } from './engine'
3 | import { ViewPorps } from '@/components/dictionaries/helpers'
4 |
5 | export const DictVocabulary: FC> = ({ result }) => (
6 | <>
7 | {result.short}
8 | {result.long}
9 | >
10 | )
11 |
12 | export default DictVocabulary
13 |
--------------------------------------------------------------------------------
/src/components/dictionaries/vocabulary/_locales.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": {
3 | "en": "Vocabulary.com",
4 | "zh-CN": "Vocabulary.com",
5 | "zh-TW": "Vocabulary.com"
6 | }
7 | }
--------------------------------------------------------------------------------
/src/components/dictionaries/vocabulary/_style.shadow.scss:
--------------------------------------------------------------------------------
1 | .dictVocabulary-Long {
2 | padding-left: 5px;
3 | border-left: 1px solid #666;
4 | }
5 |
--------------------------------------------------------------------------------
/src/components/dictionaries/vocabulary/config.ts:
--------------------------------------------------------------------------------
1 | import { DictItem } from '@/app-config/dicts'
2 |
3 | export type VocabularyConfig = DictItem
4 |
5 | export default (): VocabularyConfig => ({
6 | lang: '10000000',
7 | selectionLang: {
8 | english: true,
9 | chinese: false,
10 | japanese: false,
11 | korean: false,
12 | french: false,
13 | spanish: false,
14 | deutsch: false,
15 | others: false,
16 | matchAll: false
17 | },
18 | defaultUnfold: {
19 | english: true,
20 | chinese: true,
21 | japanese: true,
22 | korean: true,
23 | french: true,
24 | spanish: true,
25 | deutsch: true,
26 | others: true,
27 | matchAll: false
28 | },
29 | preferredHeight: 180,
30 | selectionWC: {
31 | min: 1,
32 | max: 5
33 | }
34 | })
35 |
--------------------------------------------------------------------------------
/src/components/dictionaries/vocabulary/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/components/dictionaries/vocabulary/favicon.png
--------------------------------------------------------------------------------
/src/components/dictionaries/weblio/View.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react'
2 | import { WeblioResult } from './engine'
3 | import { ViewPorps } from '@/components/dictionaries/helpers'
4 | import EntryBox from '@/components/EntryBox'
5 | import { StrElm } from '@/components/StrElm'
6 |
7 | export const DictWeblio: FC> = ({ result }) => (
8 |
9 | {result.map(({ title, def }) => (
10 | }
14 | >
15 |
16 |
17 | ))}
18 |
19 | )
20 |
21 | export default DictWeblio
22 |
--------------------------------------------------------------------------------
/src/components/dictionaries/weblio/_locales.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": {
3 | "en": "Weblio",
4 | "zh-CN": "Weblio 辞書",
5 | "zh-TW": "Weblio 辞書"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/components/dictionaries/weblio/config.ts:
--------------------------------------------------------------------------------
1 | import { DictItem } from '@/app-config/dicts'
2 |
3 | export type WeblioConfig = DictItem
4 |
5 | export default (): WeblioConfig => ({
6 | lang: '00010000',
7 | selectionLang: {
8 | english: true,
9 | chinese: true,
10 | japanese: true,
11 | korean: false,
12 | french: false,
13 | spanish: false,
14 | deutsch: false,
15 | others: false,
16 | matchAll: false
17 | },
18 | defaultUnfold: {
19 | english: true,
20 | chinese: true,
21 | japanese: true,
22 | korean: true,
23 | french: true,
24 | spanish: true,
25 | deutsch: true,
26 | others: true,
27 | matchAll: false
28 | },
29 | preferredHeight: 265,
30 | selectionWC: {
31 | min: 1,
32 | max: 20
33 | }
34 | })
35 |
--------------------------------------------------------------------------------
/src/components/dictionaries/weblio/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/components/dictionaries/weblio/favicon.png
--------------------------------------------------------------------------------
/src/components/dictionaries/weblioejje/View.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react'
2 | import { WeblioejjeResult } from './engine'
3 | import EntryBox from '@/components/EntryBox'
4 | import { ViewPorps } from '@/components/dictionaries/helpers'
5 | import { StrElm } from '@/components/StrElm'
6 |
7 | export const DictWeblioejje: FC> = ({ result }) => (
8 |
9 | {result.map((entry, i) =>
10 | entry.title ? (
11 |
12 |
13 |
14 | ) : (
15 |
16 | )
17 | )}
18 |
19 | )
20 |
21 | export default DictWeblioejje
22 |
--------------------------------------------------------------------------------
/src/components/dictionaries/weblioejje/_locales.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": {
3 | "en": "Weblio ejje",
4 | "zh-CN": "Weblio 英和和英",
5 | "zh-TW": "Weblio 英和和英"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/components/dictionaries/weblioejje/config.ts:
--------------------------------------------------------------------------------
1 | import { DictItem } from '@/app-config/dicts'
2 |
3 | export type VocabularyConfig = DictItem
4 |
5 | export default (): VocabularyConfig => ({
6 | lang: '10010000',
7 | selectionLang: {
8 | english: true,
9 | chinese: false,
10 | japanese: true,
11 | korean: false,
12 | french: false,
13 | spanish: false,
14 | deutsch: false,
15 | others: false,
16 | matchAll: false
17 | },
18 | defaultUnfold: {
19 | english: true,
20 | chinese: true,
21 | japanese: true,
22 | korean: true,
23 | french: true,
24 | spanish: true,
25 | deutsch: true,
26 | others: true,
27 | matchAll: false
28 | },
29 | preferredHeight: 400,
30 | selectionWC: {
31 | min: 1,
32 | max: 999
33 | }
34 | })
35 |
--------------------------------------------------------------------------------
/src/components/dictionaries/weblioejje/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/components/dictionaries/weblioejje/favicon.png
--------------------------------------------------------------------------------
/src/components/dictionaries/websterlearner/_locales.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": {
3 | "en": "Merriam-Webster's Learner's Dictionary",
4 | "zh-CN": "韦氏学习词典",
5 | "zh-TW": "韋氏學習字典"
6 | },
7 | "options": {
8 | "defs": {
9 | "en": "Show definitions",
10 | "zh-CN": "显示释义",
11 | "zh-TW": "顯示解釋"
12 | },
13 | "phrase": {
14 | "en": "Show phrases",
15 | "zh-CN": "显示词组",
16 | "zh-TW": "顯示片語"
17 | },
18 | "derived": {
19 | "en": "Show derived words",
20 | "zh-CN": "显示派生词",
21 | "zh-TW": "顯示衍生字"
22 | },
23 | "arts": {
24 | "en": "Show pictures",
25 | "zh-CN": "显示图片释义",
26 | "zh-TW": "顯示圖片解釋"
27 | },
28 | "related": {
29 | "en": "Show related results",
30 | "zh-CN": "失败时显示备选",
31 | "zh-TW": "失敗時顯示備選"
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/components/dictionaries/websterlearner/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/components/dictionaries/websterlearner/favicon.png
--------------------------------------------------------------------------------
/src/components/dictionaries/wikipedia/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/components/dictionaries/wikipedia/favicon.png
--------------------------------------------------------------------------------
/src/components/dictionaries/youdao/_locales.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": {
3 | "en": "Youdao Dictionary",
4 | "zh-CN": "有道词典",
5 | "zh-TW": "有道詞典"
6 | },
7 | "options": {
8 | "basic": {
9 | "en": "Show basic meaning",
10 | "zh-CN": "显示简单释义",
11 | "zh-TW": "顯示簡單解釋"
12 | },
13 | "collins": {
14 | "en": "Show Collins result",
15 | "zh-CN": "显示柯林斯双解",
16 | "zh-TW": "顯示柯林斯雙解"
17 | },
18 | "discrimination": {
19 | "en": "Show discrimination",
20 | "zh-CN": "显示词语辨析",
21 | "zh-TW": "顯示詞語辨析"
22 | },
23 | "sentence": {
24 | "en": "Show sentences",
25 | "zh-CN": "显示权威例句",
26 | "zh-TW": "顯示權威例句"
27 | },
28 | "translation": {
29 | "en": "Show translation",
30 | "zh-CN": "显示有道翻译",
31 | "zh-TW": "顯示有道翻譯"
32 | },
33 | "related": {
34 | "en": "Show related results",
35 | "zh-CN": "失败时显示备选",
36 | "zh-TW": "失敗時顯示備選"
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/components/dictionaries/youdao/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/components/dictionaries/youdao/favicon.png
--------------------------------------------------------------------------------
/src/components/dictionaries/youdaotrans/View.tsx:
--------------------------------------------------------------------------------
1 | export { MachineTrans as default } from '@/components/MachineTrans/MachineTrans'
2 |
--------------------------------------------------------------------------------
/src/components/dictionaries/youdaotrans/_locales.ts:
--------------------------------------------------------------------------------
1 | import { getMachineLocales } from '../locales'
2 |
3 | export const locales = getMachineLocales({
4 | en: 'Youdao Translate',
5 | 'zh-CN': '有道翻译',
6 | 'zh-TW': '有道翻譯'
7 | })
8 |
--------------------------------------------------------------------------------
/src/components/dictionaries/youdaotrans/_style.shadow.scss:
--------------------------------------------------------------------------------
1 | @import '@/components/MachineTrans/MachineTrans.scss';
2 |
--------------------------------------------------------------------------------
/src/components/dictionaries/youdaotrans/auth.ts:
--------------------------------------------------------------------------------
1 | export const auth = {
2 | appKey: '',
3 | key: ''
4 | }
5 |
6 | export const url = 'http://ai.youdao.com/gw.s'
7 |
--------------------------------------------------------------------------------
/src/components/dictionaries/youdaotrans/config.ts:
--------------------------------------------------------------------------------
1 | import {
2 | MachineDictItem,
3 | machineConfig
4 | } from '@/components/MachineTrans/engine'
5 | import { Language } from '@opentranslate/translator'
6 | import { Subunion } from '@/typings/helpers'
7 |
8 | export type YoudaotransLanguage = Subunion<
9 | Language,
10 | 'zh-CN' | 'en' | 'pt' | 'es' | 'ja' | 'ko' | 'fr' | 'ru'
11 | >
12 |
13 | export type YoudaotransConfig = MachineDictItem
14 |
15 | export default (): YoudaotransConfig =>
16 | machineConfig(
17 | ['zh-CN', 'en', 'pt', 'es', 'ja', 'ko', 'fr', 'ru'],
18 | {
19 | lang: '11011111'
20 | },
21 | {},
22 | {}
23 | )
24 |
--------------------------------------------------------------------------------
/src/components/dictionaries/youdaotrans/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/components/dictionaries/youdaotrans/favicon.png
--------------------------------------------------------------------------------
/src/components/dictionaries/zdic/View.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react'
2 | import { ZdicResult } from './engine'
3 | import { ViewPorps } from '@/components/dictionaries/helpers'
4 | import EntryBox from '@/components/EntryBox'
5 | import { StrElm } from '@/components/StrElm'
6 |
7 | export const DictZdic: FC> = ({ result }) => (
8 |
9 | {result.map(entry => (
10 |
11 |
12 |
13 | ))}
14 |
15 | )
16 |
17 | export default DictZdic
18 |
--------------------------------------------------------------------------------
/src/components/dictionaries/zdic/_locales.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": {
3 | "en": "汉典",
4 | "zh-CN": "汉典",
5 | "zh-TW": "漢典"
6 | },
7 | "options": {
8 | "audio": {
9 | "en": "Enable audio",
10 | "zh-CN": "开启发音",
11 | "zh-TW": "啟用發音"
12 | }
13 | },
14 | "helps": {
15 | "audio": {
16 | "en": "Referer modification is required, which may slightly impact performance.",
17 | "zh-CN": "突破外链限制需要改写 Referer,可能会轻微影响性能。",
18 | "zh-TW": "突破外鏈限制需要改寫 Referer,可能會輕微影響效能。"
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/components/dictionaries/zdic/config.ts:
--------------------------------------------------------------------------------
1 | import { DictItem } from '@/app-config/dicts'
2 |
3 | export type ZdicConfig = DictItem<{
4 | audio: boolean
5 | }>
6 |
7 | export default (): ZdicConfig => ({
8 | lang: '01000000',
9 | selectionLang: {
10 | english: false,
11 | chinese: true,
12 | japanese: false,
13 | korean: false,
14 | french: false,
15 | spanish: false,
16 | deutsch: false,
17 | others: false,
18 | matchAll: false
19 | },
20 | defaultUnfold: {
21 | english: true,
22 | chinese: true,
23 | japanese: true,
24 | korean: true,
25 | french: true,
26 | spanish: true,
27 | deutsch: true,
28 | others: true,
29 | matchAll: false
30 | },
31 | preferredHeight: 400,
32 | selectionWC: {
33 | min: 1,
34 | max: 5
35 | },
36 | options: {
37 | audio: false
38 | }
39 | })
40 |
--------------------------------------------------------------------------------
/src/components/dictionaries/zdic/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crimx/ext-saladict/ffb478cd2e3277a40edeb9470ad1c6614b010028/src/components/dictionaries/zdic/favicon.png
--------------------------------------------------------------------------------
/src/content/__fake__/env-instant-capture.ts:
--------------------------------------------------------------------------------
1 | import { createIntantCaptureStream } from '@/selection/instant-capture'
2 | import getDefaultConfig, { AppConfigMutable } from '@/app-config'
3 |
4 | const config = getDefaultConfig() as AppConfigMutable
5 | config.mode.instant.enable = true
6 | config.mode.instant.key = 'ctrl'
7 |
8 | createIntantCaptureStream(config).subscribe(console.log)
9 |
--------------------------------------------------------------------------------
/src/content/__fake__/env-select-text.ts:
--------------------------------------------------------------------------------
1 | import { createSelectTextStream } from '@/selection/select-text'
2 | import getDefaultConfig from '@/app-config'
3 |
4 | const config = getDefaultConfig()
5 |
6 | createSelectTextStream(config).subscribe(console.log)
7 |
--------------------------------------------------------------------------------
/src/content/__fake__/env.ts:
--------------------------------------------------------------------------------
1 | import faker from 'faker'
2 | import '@/selection'
3 | import { initConfig, updateConfig } from '@/_helpers/config-manager'
4 | import { initProfiles, updateProfile } from '@/_helpers/profile-manager'
5 | import { ProfileMutable } from '@/app-config/profiles'
6 | import { AppConfigMutable } from '@/app-config'
7 |
8 | browser.runtime.sendMessage['_sender'].callsFake(() => ({
9 | tab: {
10 | id: 'saladict-page'
11 | }
12 | }))
13 |
14 | initConfig().then(_config => {
15 | const config = _config as AppConfigMutable
16 | config.mode.instant.enable = true
17 | config.panelMode.direct = true
18 | updateConfig(config)
19 | })
20 | initProfiles().then(profile => {
21 | ;(profile as ProfileMutable).dicts.selected = ['bing']
22 | updateProfile(profile)
23 | })
24 |
25 | for (let i = 0; i < 10; i++) {
26 | const $p = document.createElement('p')
27 | $p.innerHTML = 'love ' + faker.lorem.paragraph()
28 | document.body.appendChild($p)
29 | }
30 |
--------------------------------------------------------------------------------
/src/content/_style.scss:
--------------------------------------------------------------------------------
1 | .saladict-div {
2 | @extend %reset-important;
3 | }
4 |
--------------------------------------------------------------------------------
/src/content/components/DictList/DictList.scss:
--------------------------------------------------------------------------------
1 | @import '../DictItem/DictItem.scss';
2 |
3 | .dictList > .dictItem:first-child > .dictItemHead {
4 | border-top-color: transparent;
5 | }
6 |
--------------------------------------------------------------------------------
/src/content/components/DictPanel/DictPanel.scss:
--------------------------------------------------------------------------------
1 | @import '@/_sass_shared/_theme.scss';
2 |
3 | .dictPanel-FloatBox-Container {
4 | position: relative;
5 | }
6 |
7 | .dictPanel-Root {
8 | display: flex;
9 | flex-direction: column;
10 | box-sizing: border-box;
11 | position: fixed;
12 | z-index: $global-zindex-dictpanel;
13 | top: 0;
14 | left: 0;
15 | overflow: hidden;
16 | text-align: initial;
17 | border-radius: 6px;
18 | background-color: inherit;
19 | box-shadow: rgba(0, 0, 0, 0.8) 0px 4px 23px -6px;
20 | }
21 |
22 | .dictPanel-Head {
23 | flex-shrink: 0;
24 | }
25 |
26 | .dictPanel-Body {
27 | flex: 1;
28 | overflow-x: hidden;
29 | overflow-y: scroll;
30 | // font-size: 0; // https://bugzilla.mozilla.org/show_bug.cgi?id=1573030
31 | -webkit-overflow-scrolling: touch;
32 | }
33 |
34 | @import '@/_sass_shared/_fancy-scrollbar.scss';
35 |
36 | @import '../MenuBar/MenuBar.scss';
37 | @import '../MtaBox/MtaBox.scss';
38 | @import '../DictList/DictList.scss';
39 | @import '../WaveformBox/WaveformBox.scss';
40 |
--------------------------------------------------------------------------------
/src/content/components/DictPanel/DictPanel.shadow.scss:
--------------------------------------------------------------------------------
1 | @import '@/components/ShadowPortal/ShadowPortal.scss';
2 | @import './DictPanel.scss';
3 |
4 | .dictPanel-DragMask {
5 | position: fixed;
6 | z-index: $global-zindex-dictpanel;
7 | top: 0;
8 | left: 0;
9 | bottom: 0;
10 | right: 0;
11 | margin: auto;
12 | background: rgba(225, 225, 225, 0.01);
13 | cursor: grabbing;
14 | cursor: -moz-grabbing;
15 | cursor: -webkit-grabbing;
16 | }
17 |
18 | .dictPanel-Root {
19 | @include isAnimate {
20 | transition: width 0.4s, height 0.4s, opacity 0.4s,
21 | top 0.4s cubic-bezier(0.55, 0.82, 0.63, 0.95),
22 | left 0.4s cubic-bezier(0.4, 0.9, 0.71, 1.02);
23 | }
24 |
25 | &.isDragging {
26 | @include isAnimate {
27 | transition: width 0.4s, height 0.4s, opacity 0.4s;
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/content/components/DictPanel/DictPanelStandalone.scss:
--------------------------------------------------------------------------------
1 | @import './DictPanel.scss';
2 |
3 | .dictPanel-FloatBox-Container {
4 | position: absolute;
5 | top: 0;
6 | left: 0;
7 | }
8 |
9 | .dictPanel-Root {
10 | position: relative !important;
11 | top: 0 !important;
12 | left: 0 !important;
13 | width: 450px;
14 | height: 500px;
15 | --panel-width: 450px;
16 | --panel-max-height: 500px;
17 | border-radius: 0;
18 | box-shadow: rgba(0, 0, 0, 0.8) 0px 5px 20px -12px;
19 |
20 | @include isAnimate {
21 | transition: height 0.4s;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/content/components/MenuBar/MenuBar.scss:
--------------------------------------------------------------------------------
1 | @import './MenubarBtns.scss';
2 | @import './SearchBox.scss';
3 | @import './Profiles.scss';
4 |
5 | .menuBar {
6 | display: flex;
7 | align-items: center;
8 | position: relative;
9 | height: 30px;
10 | padding: 0 3px;
11 | font-size: 14px;
12 | background-color: var(--color-brand);
13 | }
14 |
15 | .menuBar-DragArea {
16 | flex: 3;
17 | align-self: stretch;
18 | user-select: none;
19 | // prevent scrolling
20 | touch-action: none;
21 | cursor: move;
22 | }
23 |
--------------------------------------------------------------------------------
/src/content/components/MenuBar/Profiles.scss:
--------------------------------------------------------------------------------
1 | @import '@/components/HoverBox/HoverBox.scss';
2 | @import './MenubarBtns.scss';
3 |
4 | .menuBar-ProfileItem {
5 | position: relative;
6 | padding-left: 10px;
7 | color: var(--color-font);
8 |
9 | &.isActive::before {
10 | content: '';
11 | position: absolute;
12 | top: 50%;
13 | transform: translateY(-55%);
14 | left: -5px;
15 | width: 0;
16 | height: 0;
17 | border-left: 10px solid currentColor;
18 | border-top: 5px solid transparent;
19 | border-bottom: 5px solid transparent;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/content/components/MenuBar/SearchBox.scss:
--------------------------------------------------------------------------------
1 | @import './Suggest.scss';
2 | @import './MenubarBtns.scss';
3 |
4 | .menuBar-SearchBox_Wrap {
5 | flex: 2;
6 | position: relative;
7 |
8 | &.isExpand {
9 | flex: 7;
10 | }
11 |
12 | @include isAnimate {
13 | transition: flex 0.6s;
14 | }
15 | }
16 |
17 | .menuBar-SearchBox {
18 | width: 100%;
19 | box-sizing: border-box;
20 | padding: 0 5px;
21 | border: 0 none;
22 | outline: 0 none;
23 | color: #fff;
24 | background-color: rgba(225, 225, 225, 0.1);
25 | }
26 |
27 | .menuBar-SearchBox_Suggests {
28 | position: absolute;
29 | left: 0;
30 | top: 30px;
31 | z-index: 1000;
32 | }
33 |
34 | .csst-menuBar-SearchBox_Suggests {
35 | @include isAnimate(-enter) {
36 | opacity: 0;
37 | transition: opacity 0.4s;
38 | }
39 |
40 | @include isAnimate(-enter-active, -exit) {
41 | opacity: 1;
42 | transition: opacity 0.4s;
43 | }
44 |
45 | @include isAnimate(-exit-active) {
46 | opacity: 0;
47 | transition: opacity 0.4s;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/content/components/MenuBar/Suggest.scss:
--------------------------------------------------------------------------------
1 | @import '@/components/FloatBox/FloatBox.scss';
2 |
3 | .menuBar-SuggestsEntry {
4 | margin-right: 1.5em;
5 | color: #f9690e;
6 | }
7 |
8 | .menuBar-SuggestsExplain {
9 | color: var(--color-font);
10 | }
11 |
--------------------------------------------------------------------------------
/src/content/components/WordEditor/WordEditor.scss:
--------------------------------------------------------------------------------
1 | /*-----------------------------------------------*\
2 | Variables
3 | \*-----------------------------------------------*/
4 | @import '@/_sass_shared/_theme.scss';
5 |
6 | /*-----------------------------------------------*\
7 | Libs
8 | \*-----------------------------------------------*/
9 | @import '~normalize-scss';
10 |
11 | /*-----------------------------------------------*\
12 | Components
13 | \*-----------------------------------------------*/
14 | @import './Notes.scss';
15 |
--------------------------------------------------------------------------------
/src/content/components/WordEditor/WordEditor.shadow.scss:
--------------------------------------------------------------------------------
1 | @import './WordEditor.scss';
2 | @import '@/components/ShadowPortal/ShadowPortal.scss';
3 |
--------------------------------------------------------------------------------
/src/content/components/WordEditor/WordEditor.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react'
2 | import { Notes, NotesProps } from './Notes'
3 | import { SALADICT_EXTERNAL } from '@/_helpers/saladict'
4 |
5 | export interface WordEditorProps extends NotesProps {}
6 |
7 | export const WordEditor: FC = props => {
8 | return (
9 |
10 |
11 |
12 | )
13 | }
14 |
--------------------------------------------------------------------------------
/src/content/components/WordEditor/WordEditorStandalone.container.tsx:
--------------------------------------------------------------------------------
1 | import { connect } from 'react-redux'
2 | import { MapStateToProps } from 'react-retux'
3 | import { StoreState } from '@/content/redux/modules'
4 | import { WordEditor, WordEditorProps } from './WordEditor'
5 |
6 | const onClose = () => {
7 | window.close()
8 | }
9 |
10 | const mapStateToProps: MapStateToProps<
11 | StoreState,
12 | WordEditorProps
13 | > = state => ({
14 | darkMode: state.config.darkMode,
15 | containerWidth: window.innerWidth,
16 | ctxTrans: state.config.ctxTrans,
17 | wordEditor: state.wordEditor,
18 | onClose
19 | })
20 |
21 | export const WordEditorStandaloneContainer = connect(mapStateToProps)(
22 | WordEditor
23 | )
24 |
25 | export default WordEditorStandaloneContainer
26 |
--------------------------------------------------------------------------------
/src/content/redux/epics/utils.ts:
--------------------------------------------------------------------------------
1 | import { Observable } from 'rxjs'
2 | import { Epic as RawEpic, ofType as rawOfType } from 'redux-observable'
3 | import { StoreAction, StoreActionType, StoreState } from '../modules'
4 |
5 | /** Tailored `Epic` for the store. */
6 | export type Epic<
7 | TOutType extends StoreActionType = StoreActionType,
8 | TDeps = any
9 | > = RawEpic, StoreState, TDeps>
10 |
11 | /**
12 | * Tailored `ofType` for the store.
13 | * Now you can use `ofType` directly without the need to
14 | * manually offer types each time.
15 | */
16 | export const ofType = rawOfType as <
17 | TInAction extends StoreAction,
18 | TTypes extends StoreActionType[] = StoreActionType[],
19 | TOutAction extends StoreAction = StoreAction
20 | >(
21 | ...types: TTypes
22 | ) => (source: Observable) => Observable
23 |
--------------------------------------------------------------------------------
/src/history/env.ts:
--------------------------------------------------------------------------------
1 | export {}
2 |
3 | window.__SALADICT_INTERNAL_PAGE__ = true
4 |
--------------------------------------------------------------------------------
/src/history/index.tsx:
--------------------------------------------------------------------------------
1 | import './env'
2 | import '@/selection'
3 |
4 | import React from 'react'
5 | import { WordPage } from '@/components/WordPage'
6 | import { initAntdRoot } from '@/components/AntdRoot'
7 |
8 | document.title = 'Saladict History'
9 |
10 | initAntdRoot(() => , '/wordpage/history')
11 |
--------------------------------------------------------------------------------
/src/manifest/chrome.manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "background": {
3 | "persistent": true
4 | },
5 | "options_ui": {
6 | "chrome_style": false
7 | },
8 | "optional_permissions": [
9 | "background"
10 | ],
11 | "content_security_policy": "script-src 'self' chrome-extension://hfjbmagddngcpeloejdejnfgbamkjaeg/ chrome-extension://aibcglbfblnogfjhbcmmpobjhnomhcdo/; object-src 'self'",
12 | "incognito": "split",
13 | "update_url": "https://clients2.google.com/service/update2/crx",
14 | "minimum_chrome_version": "63"
15 | }
16 |
--------------------------------------------------------------------------------
/src/manifest/edge.manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "background": {
3 | "persistent": true
4 | },
5 | "options_ui": {
6 | "chrome_style": false
7 | },
8 | "optional_permissions": [
9 | "background"
10 | ],
11 | "content_security_policy": "script-src 'self' chrome-extension://hfjbmagddngcpeloejdejnfgbamkjaeg/ chrome-extension://aibcglbfblnogfjhbcmmpobjhnomhcdo/; object-src 'self'",
12 | "incognito": "split",
13 | "update_url": "https://edge.microsoft.com/extensionwebstorebase/v1/crx"
14 | }
15 |
--------------------------------------------------------------------------------
/src/manifest/firefox.manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "options_ui": {
3 | "browser_style": false
4 | },
5 | "applications": {
6 | "gecko": {
7 | "id": "saladict@crimx.com",
8 | "strict_min_version": "67.0"
9 | }
10 | },
11 | "content_security_policy": "script-src 'self'; object-src 'self'"
12 | }
13 |
--------------------------------------------------------------------------------
/src/manifest/safari.manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "background": {
3 | "persistent": true
4 | },
5 | "options_ui": {
6 | "chrome_style": false
7 | },
8 | "optional_permissions": [
9 | "background"
10 | ],
11 | "incognito": "split"
12 | }
13 |
--------------------------------------------------------------------------------
/src/notebook/env.ts:
--------------------------------------------------------------------------------
1 | export {}
2 |
3 | window.__SALADICT_INTERNAL_PAGE__ = true
4 |
--------------------------------------------------------------------------------
/src/notebook/index.tsx:
--------------------------------------------------------------------------------
1 | import './env'
2 | import '@/selection'
3 |
4 | import React from 'react'
5 | import { WordPage } from '@/components/WordPage'
6 | import { initAntdRoot } from '@/components/AntdRoot'
7 |
8 | document.title = 'Saladict Notebook'
9 |
10 | initAntdRoot(() => , '/wordpage/notebook')
11 |
--------------------------------------------------------------------------------
/src/options/__fake__/env.ts:
--------------------------------------------------------------------------------
1 | import { initConfig } from '@/_helpers/config-manager'
2 | import { initProfiles } from '@/_helpers/profile-manager'
3 | import { browser } from '../../../test/helper'
4 | import packagejson from '../../../package.json'
5 |
6 | browser.runtime.getManifest.callsFake(() => ({
7 | version: packagejson.version
8 | }))
9 |
10 | browser.permissions.contains.callsFake(() => Promise.resolve(true))
11 | browser.permissions.request.callsFake(() => Promise.resolve(false))
12 |
13 | initConfig()
14 | initProfiles()
15 |
--------------------------------------------------------------------------------
/src/options/components/BtnPreview/_style.scss:
--------------------------------------------------------------------------------
1 | .btn-preview {
2 | position: fixed !important;
3 | right: 30px;
4 | bottom: 60px;
5 | border: none;
6 | color: #000 !important;
7 | background: #fff !important;
8 | box-shadow: 0 3px 6px -4px rgba(0, 0, 0, 0.12),
9 | 0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 9px 28px 8px rgba(0, 0, 0, 0.05);
10 | }
11 |
12 | .btn-preview-fade-enter {
13 | opacity: 0;
14 | transition: opacity 0.5s;
15 | }
16 |
17 | .btn-preview-fade-enter-active,
18 | .btn-preview-fade-exit {
19 | opacity: 1;
20 | transition: opacity 0.5s;
21 | }
22 |
23 | .btn-preview-fade-exit-active {
24 | opacity: 0;
25 | transition: opacity 0.5s;
26 | }
27 |
--------------------------------------------------------------------------------
/src/options/components/Entries/Dictionaries/DictTitle/_style.scss:
--------------------------------------------------------------------------------
1 | .saladict-dict-title {
2 | word-break: break-all;
3 |
4 | & > * {
5 | word-break: keep-all;
6 | white-space: nowrap;
7 | }
8 | }
9 |
10 | .saladict-dict-title-icon {
11 | width: 1.3em;
12 | height: 1.3em;
13 | margin-right: 5px;
14 | vertical-align: text-bottom;
15 | }
16 |
17 | .saladict-dict-title-link {
18 | color: currentColor;
19 | }
20 |
21 | .saladict-dict-langs-char {
22 | margin-left: 5px;
23 | padding: 0 2px;
24 | font-size: 0.92em;
25 | color: #777;
26 | border: 1px solid #777;
27 | border-radius: 2px;
28 | }
29 |
--------------------------------------------------------------------------------
/src/options/components/EntryError.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC, useEffect } from 'react'
2 | import { FrownOutlined } from '@ant-design/icons'
3 | import { message } from '@/_helpers/browser-api'
4 |
5 | export const EntryError: FC = () => {
6 | useEffect(() => {
7 | message.self.send({ type: 'CLOSE_PANEL' })
8 | }, [])
9 |
10 | return (
11 |
20 |
23 |
Entry Not Found
24 |
25 | )
26 | }
27 |
--------------------------------------------------------------------------------
/src/options/components/EntrySideBar/_style.scss:
--------------------------------------------------------------------------------
1 | @import '@/_sass_shared/_fancy-scrollbar.scss';
2 |
3 | .entry-sidebar {
4 | height: calc(100vh - 64px);
5 | background: transparent;
6 |
7 | &.isAffixed {
8 | height: 100vh;
9 | }
10 |
11 | @media (hover: hover) {
12 | overflow-y: hidden;
13 |
14 | &:hover {
15 | overflow-y: auto;
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/options/components/Header/HeadInfo/AckList.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { List } from 'antd'
3 | import { useTranslate } from '@/_helpers/i18n'
4 | import { acknowledgement } from '@/options/acknowledgement'
5 |
6 | export const AckList = React.memo(() => {
7 | const { t } = useTranslate('options')
8 | return (
9 | (
11 |
17 | ))}
18 | renderItem={item => {item}}
19 | />
20 | )
21 | })
22 |
--------------------------------------------------------------------------------
/src/options/components/Header/_style.scss:
--------------------------------------------------------------------------------
1 | .options-header {
2 | max-width: 1440px;
3 | height: 100%;
4 | margin: 0 auto;
5 | display: flex;
6 | justify-content: space-between;
7 | align-items: center;
8 | line-height: 1.2;
9 |
10 | @media screen and (max-width: 800px) {
11 | margin: 0 -20px !important;
12 | }
13 |
14 | @media screen and (max-width: 600px) {
15 | margin: 0 -40px !important;
16 | }
17 |
18 | > * {
19 | color: #fff;
20 | }
21 | }
22 |
23 | .options-header-title {
24 | text-align: right;
25 |
26 | h1 {
27 | font-size: 1.5em;
28 | color: #fff;
29 | margin: 0;
30 | }
31 |
32 | span {
33 | opacity: 0.65;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/options/components/InputNumberGroup/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react'
2 | import { InputNumber } from 'antd'
3 | import { InputNumberProps } from 'antd/lib/input-number'
4 |
5 | import './_style.scss'
6 |
7 | export interface InputNumberGroupProps extends InputNumberProps {
8 | suffix?: React.ReactNode
9 | }
10 |
11 | export const InputNumberGroup: FC = props => {
12 | const { suffix, ...restProps } = props
13 | return (
14 |
15 |
16 |
17 | {suffix && {suffix}}
18 |
19 |
20 | )
21 | }
22 |
--------------------------------------------------------------------------------
/src/options/components/SaladictForm/SaveBtn.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react'
2 | import { Button } from 'antd'
3 | import { useObservableState } from 'observable-hooks'
4 | import { useTranslate } from '@/_helpers/i18n'
5 | import { uploadStatus$ } from '@/options/helpers/upload'
6 |
7 | /**
8 | * Move the button out as independent component to reduce
9 | * re-rendering of the whole component.
10 | */
11 | export const SaveBtn: FC = () => {
12 | const { t } = useTranslate('common')
13 | const uploadStatus = useObservableState(uploadStatus$, 'idle')
14 |
15 | return (
16 |
23 | )
24 | }
25 |
--------------------------------------------------------------------------------
/src/options/components/SaladictForm/_style.scss:
--------------------------------------------------------------------------------
1 | .saladict-form-btns {
2 | margin-top: 50px;
3 |
4 | button {
5 | margin-right: 8px;
6 | margin-bottom: 12px;
7 | }
8 | }
9 |
10 | .saladict-hide {
11 | display: none !important;
12 | }
13 |
14 | .saladict-form-danger-extra .ant-form-item-extra {
15 | color: #c0392b;
16 | }
17 |
--------------------------------------------------------------------------------
/src/options/components/SortableList/_style.scss:
--------------------------------------------------------------------------------
1 | .sortable-list-item {
2 | button,
3 | .ant-radio-inner,
4 | .ant-radio-inner::after {
5 | transition: none !important;
6 | }
7 |
8 | .ant-btn-icon-only.ant-btn-sm {
9 | > * {
10 | font-size: 16px;
11 | }
12 |
13 | .anticon svg {
14 | display: block;
15 | }
16 | }
17 | }
18 |
19 | .sortable-list-item-btns {
20 | min-width: fit-content;
21 | }
22 |
--------------------------------------------------------------------------------
/src/options/components/SortableList/reorder.ts:
--------------------------------------------------------------------------------
1 | export function reorder(
2 | list: T,
3 | startIndex: number,
4 | endIndex: number
5 | ) {
6 | const result = Array.from(list)
7 | const [removed] = result.splice(startIndex, 1)
8 | result.splice(endIndex, 0, removed)
9 | return result
10 | }
11 |
--------------------------------------------------------------------------------
/src/options/env.ts:
--------------------------------------------------------------------------------
1 | export {}
2 |
3 | window.__SALADICT_INTERNAL_PAGE__ = true
4 | window.__SALADICT_OPTIONS_PAGE__ = true
5 | window.__SALADICT_LAST_SEARCH__ = ''
6 |
--------------------------------------------------------------------------------
/src/options/helpers/change-entry.ts:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export const ChangeEntryContext = React.createContext<(entry: string) => void>(
4 | null as any
5 | )
6 |
--------------------------------------------------------------------------------
/src/options/helpers/panel-store.ts:
--------------------------------------------------------------------------------
1 | import { useSelector } from 'react-redux'
2 | import { StoreState } from '@/content/redux/modules'
3 |
4 | const pickIsShowDictPanel = (state: StoreState): boolean =>
5 | state.isShowDictPanel
6 |
7 | export const useIsShowDictPanel = () => useSelector(pickIsShowDictPanel)
8 |
--------------------------------------------------------------------------------
/src/options/helpers/use-form-dirty.ts:
--------------------------------------------------------------------------------
1 | const formDirty = {
2 | value: false
3 | }
4 |
5 | export const setFormDirty = (value: boolean) => {
6 | formDirty.value = value
7 | }
8 |
9 | export const useFormDirty = (): Readonly => formDirty
10 |
--------------------------------------------------------------------------------
/src/options/index.tsx:
--------------------------------------------------------------------------------
1 | import './env'
2 | import '@/selection'
3 |
4 | import React from 'react'
5 |
6 | import { initAntdRoot } from '@/components/AntdRoot'
7 | import { MainEntry } from './components/MainEntry'
8 |
9 | import './_style.scss'
10 |
11 | document.title = 'Saladict Options'
12 |
13 | initAntdRoot(() => )
14 |
--------------------------------------------------------------------------------
/src/popup/__fake__/_style.scss:
--------------------------------------------------------------------------------
1 | body {
2 | background: #ddd;
3 | }
4 |
--------------------------------------------------------------------------------
/src/popup/__fake__/env.ts:
--------------------------------------------------------------------------------
1 | import '../index'
2 | import './_style.scss'
3 | import { initConfig, updateConfig } from '@/_helpers/config-manager'
4 |
5 | async function main() {
6 | const config = await initConfig()
7 | await updateConfig({
8 | ...config,
9 | darkMode: true
10 | })
11 | }
12 |
13 | main()
14 |
--------------------------------------------------------------------------------
/src/popup/env.ts:
--------------------------------------------------------------------------------
1 | export {}
2 |
3 | window.__SALADICT_INTERNAL_PAGE__ = true
4 | window.__SALADICT_POPUP_PAGE__ = true
5 |
--------------------------------------------------------------------------------
/src/quick-search/env.ts:
--------------------------------------------------------------------------------
1 | export {}
2 |
3 | window.__SALADICT_INTERNAL_PAGE__ = true
4 | window.__SALADICT_QUICK_SEARCH_PAGE__ = true
5 |
--------------------------------------------------------------------------------
/src/quick-search/quick-search.scss:
--------------------------------------------------------------------------------
1 | @import '@/content/components/DictPanel/DictPanelStandalone.scss';
2 |
3 | html,
4 | body,
5 | #root {
6 | position: static;
7 | height: 100%;
8 | margin: 0;
9 | padding: 0;
10 | // hide white spaces
11 | font-size: 0;
12 | }
13 |
14 | #root {
15 | overflow: hidden;
16 | }
17 |
18 | .popup-root {
19 | overflow: hidden;
20 | display: flex;
21 | flex-direction: column-reverse;
22 | width: 450px;
23 | height: 550px;
24 | font-size: 14px;
25 | }
26 |
--------------------------------------------------------------------------------
/src/selection/quick-search.ts:
--------------------------------------------------------------------------------
1 | import { EMPTY, merge } from 'rxjs'
2 | import { share, buffer, debounceTime, filter } from 'rxjs/operators'
3 | import { AppConfig } from '@/app-config'
4 | import { isStandalonePage, isOptionsPage } from '@/_helpers/saladict'
5 | import { whenKeyPressed, isQSKey } from './helper'
6 |
7 | /**
8 | * Listen to triple-ctrl shortcut which opens quick search panel.
9 | * Pressing ctrl/command key more than three times within 500ms
10 | * trigers triple-ctrl.
11 | */
12 | export function createQuickSearchStream(config: AppConfig | null) {
13 | if (!config || !config.tripleCtrl || isStandalonePage() || isOptionsPage()) {
14 | return EMPTY
15 | }
16 |
17 | const qsKeyPressed$$ = share()(whenKeyPressed(isQSKey))
18 |
19 | return qsKeyPressed$$.pipe(
20 | buffer(
21 | merge(
22 | debounceTime(500)(qsKeyPressed$$), // collect after 0.5s
23 | whenKeyPressed(e => !isQSKey(e)) // other key pressed
24 | )
25 | ),
26 | filter(group => group.length >= 3)
27 | )
28 | }
29 |
--------------------------------------------------------------------------------
/src/typings/css.d.ts:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line
2 | import * as CSS from 'csstype'
3 |
4 | declare module 'csstype' {
5 | interface Properties {
6 | '--panel-width'?: string
7 | '--panel-max-height'?: string
8 | '--panel-font-size'?: string
9 | '--color-brand'?: string
10 | '--color-font'?: string
11 | '--color-background'?: string
12 | '--color-rgb-background'?: string
13 | '--color-divider'?: string
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/typings/global.d.ts:
--------------------------------------------------------------------------------
1 | interface Window {
2 | browser: typeof browser
3 |
4 | __SALADICT_PANEL_LOADED__?: boolean
5 | __SALADICT_SELECTION_LOADED__?: boolean
6 |
7 | // For self page messaging
8 | pageId?: number | string
9 | faviconURL?: string
10 | pageTitle?: string
11 | pageURL?: string
12 |
13 | __SALADICT_BACKGROUND_PAGE__?: boolean
14 | __SALADICT_INTERNAL_PAGE__?: boolean
15 | __SALADICT_OPTIONS_PAGE__?: boolean
16 | __SALADICT_POPUP_PAGE__?: boolean
17 | __SALADICT_QUICK_SEARCH_PAGE__?: boolean
18 | __SALADICT_PDF_PAGE__?: boolean
19 |
20 | // Options page
21 | __SALADICT_LAST_SEARCH__?: string
22 |
23 | // eslint-disable-next-line
24 | __webpack_public_path__?: string
25 | }
26 |
--------------------------------------------------------------------------------
/src/typings/helpers.ts:
--------------------------------------------------------------------------------
1 | interface DeepReadonlyArray extends ReadonlyArray> {}
2 |
3 | type DeepReadonlyObject = {
4 | readonly [P in keyof T]: DeepReadonly
5 | }
6 |
7 | export type DeepReadonly = T extends (infer R)[]
8 | ? DeepReadonlyArray
9 | : T extends Function
10 | ? T
11 | : T extends object
12 | ? DeepReadonlyObject
13 | : T
14 |
15 | export type Diff = Exclude
16 |
17 | export type Omit = Pick>
18 |
19 | export type Mutable = { -readonly [P in keyof T]: T[P] }
20 |
21 | export type UnionKeys = T extends any ? keyof T : never
22 | export type UnionPick> = T extends any
23 | ? Pick>
24 | : never
25 |
26 | export type Subunion = U
27 |
28 | export const objectKeys = Object.keys as (o: T) => Extract[]
29 |
--------------------------------------------------------------------------------
/src/word-editor/env.ts:
--------------------------------------------------------------------------------
1 | export {}
2 |
3 | window.__SALADICT_INTERNAL_PAGE__ = true
4 |
--------------------------------------------------------------------------------
/src/word-editor/word-editor.scss:
--------------------------------------------------------------------------------
1 | @import '@/content/components/WordEditor/WordEditor.scss';
2 |
3 | html,
4 | body,
5 | #root {
6 | position: static;
7 | height: 100%;
8 | margin: 0;
9 | padding: 0;
10 | // hide white spaces
11 | font-size: 0;
12 | }
13 |
14 | #root {
15 | overflow: hidden;
16 | }
17 |
18 | .wordEditorPanel-Container {
19 | width: 100% !important;
20 | }
21 |
22 | .wordEditorPanel {
23 | width: 100% !important;
24 | height: 100vh !important;
25 | max-width: unset;
26 | max-height: unset;
27 | border-radius: 0;
28 | box-shadow: none;
29 | }
30 |
--------------------------------------------------------------------------------
/test/helper.ts:
--------------------------------------------------------------------------------
1 | import * as SinonChrome from 'sinon-chrome'
2 |
3 | export const browser = (window.browser as unknown) as typeof SinonChrome
4 |
--------------------------------------------------------------------------------
/test/specs/_helpers/chs-to-chz.spec.ts:
--------------------------------------------------------------------------------
1 | import chsToChz from '@/_helpers/chs-to-chz'
2 |
3 | describe('Chs to Chz', () => {
4 | it('should convert chs to chz', () => {
5 | expect(chsToChz('龙龟')).toBe('龍龜')
6 | })
7 | })
8 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/ahdict/fixtures.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | files: [
3 | ['comment.html', 'https://ahdictionary.com/word/search.html?q=comment'],
4 | ['love.html', 'https://ahdictionary.com/word/search.html?q=love'],
5 | ['salad.html', 'https://ahdictionary.com/word/search.html?q=salad']
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/ahdict/requests.mock.ts:
--------------------------------------------------------------------------------
1 | import { MockRequest } from '@/components/dictionaries/helpers'
2 |
3 | export const mockSearchTexts = ['comment', 'love', 'salad']
4 |
5 | export const mockRequest: MockRequest = mock => {
6 | mock
7 | .onGet(/ahdictionary.+comment$/)
8 | .reply(200, require('!raw-loader!./response/comment.html').default)
9 |
10 | mock
11 | .onGet(/ahdictionary.+love$/)
12 | .reply(200, require('!raw-loader!./response/love.html').default)
13 |
14 | mock
15 | .onGet(/ahdictionary.+salad$/)
16 | .reply(200, require('!raw-loader!./response/salad.html').default)
17 | }
18 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/bing/fixtures.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | files: [
3 | [
4 | 'lex.html',
5 | 'https://cn.bing.com/dict/clientsearch?mkt=zh-CN&setLang=zh&form=BDVEHC&ClientVer=BDDTV3.5.1.4320&q=love'
6 | ],
7 | [
8 | 'machine.html',
9 | `https://cn.bing.com/dict/clientsearch?mkt=zh-CN&setLang=zh&form=BDVEHC&ClientVer=BDDTV3.5.1.4320&q=${encodeURIComponent(
10 | 'lose yourself in the dark'
11 | )}`
12 | ],
13 | [
14 | 'related.html',
15 | 'https://cn.bing.com/dict/clientsearch?mkt=zh-CN&setLang=zh&form=BDVEHC&ClientVer=BDDTV3.5.1.4320&q=lovxx'
16 | ]
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/bing/requests.mock.ts:
--------------------------------------------------------------------------------
1 | import { MockRequest } from '@/components/dictionaries/helpers'
2 |
3 | export const mockSearchTexts = ['love', 'machine', 'related']
4 |
5 | export const mockRequest: MockRequest = mock => {
6 | mock
7 | .onGet(/bing\.com.+love$/)
8 | .reply(200, require('!raw-loader!./response/lex.html').default)
9 |
10 | mock
11 | .onGet(/bing\.com.+machine$/)
12 | .reply(200, require('!raw-loader!./response/machine.html').default)
13 |
14 | mock
15 | .onGet(/bing\.com.+related$/)
16 | .reply(200, require('!raw-loader!./response/related.html').default)
17 | }
18 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/cambridge/fixtures.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | files: [
3 | [
4 | 'catch-zht.html',
5 | 'https://dictionary.cambridge.org/zht/%E6%90%9C%E7%B4%A2/direct/?datasetsearch=english-chinese-traditional&q=catch'
6 | ],
7 | [
8 | 'house-zhs.html',
9 | 'https://dictionary.cambridge.org/zhs/%E6%90%9C%E7%B4%A2/direct/?datasetsearch=english-chinese-simplified&q=house'
10 | ],
11 | [
12 | 'love.html',
13 | 'https://dictionary.cambridge.org/search/direct/?datasetsearch=english&q=love'
14 | ],
15 | [
16 | 'jumblish.html',
17 | 'https://dictionary.cambridge.org/search/direct/?datasetsearch=english&q=jumblish'
18 | ]
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/cambridge/requests.mock.ts:
--------------------------------------------------------------------------------
1 | import { MockRequest } from '@/components/dictionaries/helpers'
2 |
3 | export const mockSearchTexts = ['jumblish', 'catch-zht', 'house-zhs', 'love']
4 |
5 | export const mockRequest: MockRequest = mock => {
6 | mock.onGet(/cambridge/).reply(info => {
7 | return [
8 | 200,
9 | require('!raw-loader!./response/' +
10 | new URL(info.url!).searchParams.get('q') +
11 | '.html').default
12 | ]
13 | })
14 | }
15 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/cnki/engine.spec.ts:
--------------------------------------------------------------------------------
1 | import { retry } from '../helpers'
2 | import { search } from '@/components/dictionaries/cnki/engine'
3 | import { getDefaultConfig } from '@/app-config'
4 | import { getDefaultProfile } from '@/app-config/profiles'
5 |
6 | describe('Dict/CNKI/engine', () => {
7 | it('should parse result correctly', () => {
8 | return retry(() =>
9 | search('love', getDefaultConfig(), getDefaultProfile(), {
10 | isPDF: false
11 | }).then(({ result, audio }) => {
12 | expect(audio).toBeUndefined()
13 | expect(result.dict.length).toBeGreaterThan(0)
14 | expect(result.senbi.length).toBeGreaterThan(0)
15 | expect(result.seneng.length).toBeGreaterThan(0)
16 | })
17 | )
18 | })
19 | })
20 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/cnki/fixtures.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | files: [
3 | ['love.html', 'http://dict.cnki.net/old/dict_result.aspx?scw=love']
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/cnki/requests.mock.ts:
--------------------------------------------------------------------------------
1 | import { MockRequest } from '@/components/dictionaries/helpers'
2 |
3 | export const mockSearchTexts = ['love']
4 |
5 | export const mockRequest: MockRequest = mock => {
6 | mock.onGet(/cnki/).reply(info => {
7 | return [
8 | 200,
9 | require('!raw-loader!./response/' +
10 | new URL(info.url!).searchParams.get('searchword') +
11 | '.html').default
12 | ]
13 | })
14 | }
15 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/cobuild/engine.spec.ts:
--------------------------------------------------------------------------------
1 | import { retry } from '../helpers'
2 | import { search } from '@/components/dictionaries/cobuild/engine'
3 | import { getDefaultConfig } from '@/app-config'
4 | import { getDefaultProfile, ProfileMutable } from '@/app-config/profiles'
5 |
6 | describe('Dict/COBUILD/engine', () => {
7 | it('should parse result correctly', () => {
8 | const profile = getDefaultProfile() as ProfileMutable
9 | return retry(() =>
10 | search('love', getDefaultConfig(), profile, { isPDF: false }).then(
11 | searchResult => {
12 | expect(searchResult.result).toBeTruthy()
13 | }
14 | )
15 | )
16 | })
17 | })
18 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/cobuild/fixtures.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | files: [
3 | ['how.html', 'https://www.collinsdictionary.com/dictionary/english/how'],
4 | [
5 | 'love.html',
6 | 'https://www.collinsdictionary.com/zh/dictionary/english/love'
7 | ]
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/cobuild/requests.mock.ts:
--------------------------------------------------------------------------------
1 | import { MockRequest } from '@/components/dictionaries/helpers'
2 |
3 | export const mockSearchTexts = ['test']
4 |
5 | export const mockRequest: MockRequest = mock => {
6 | mock
7 | .onGet(/collinsdictionary\.com\/zh/)
8 | .reply(200, require('!raw-loader!./response/love.html').default)
9 |
10 | mock
11 | .onGet(/collinsdictionary/)
12 | .reply(200, require('!raw-loader!./response/how.html').default)
13 | }
14 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/etymonline/fixtures.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | files: [
3 | ['love-word.html', 'https://www.etymonline.com/word/love'],
4 | ['love.html', 'http://www.etymonline.com/search?q=love']
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/etymonline/requests.mock.ts:
--------------------------------------------------------------------------------
1 | import { MockRequest } from '@/components/dictionaries/helpers'
2 |
3 | export const mockSearchTexts = ['love-word', 'love']
4 |
5 | export const mockRequest: MockRequest = mock => {
6 | mock.onGet(/etymonline.+\/word\//).reply(info => {
7 | return /love-word/.test(info.url || '')
8 | ? [200, require('!raw-loader!./response/love-word.html').default]
9 | : [404]
10 | })
11 |
12 | mock.onGet(/etymonline.+\/search\?/).reply(info => {
13 | return /love-word/.test(info.url || '')
14 | ? [404]
15 | : [200, require('!raw-loader!./response/love-word.html').default]
16 | })
17 | }
18 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/eudic/engine.spec.ts:
--------------------------------------------------------------------------------
1 | import { retry } from '../helpers'
2 | import { search } from '@/components/dictionaries/eudic/engine'
3 | import { getDefaultConfig } from '@/app-config'
4 | import { getDefaultProfile } from '@/app-config/profiles'
5 |
6 | describe('Dict/Eudic/engine', () => {
7 | it('should parse result correctly', async () => {
8 | return retry(() =>
9 | search('love', getDefaultConfig(), getDefaultProfile(), {
10 | isPDF: false
11 | }).then(searchResult => {
12 | expect(searchResult.audio && typeof searchResult.audio.us).toBe(
13 | 'string'
14 | )
15 | expect(searchResult.result).toHaveLength(10)
16 | const item = searchResult.result[0]
17 | expect(typeof item.chs).toBe('string')
18 | expect(typeof item.eng).toBe('string')
19 | expect(typeof item.mp3).toBe('string')
20 | expect(typeof item.channel).toBe('string')
21 | })
22 | )
23 | })
24 | })
25 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/eudic/fixtures.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | files: [
3 | ['love.html', 'https://dict.eudic.net/dicts/en/love'],
4 | [
5 | 'sentences.html',
6 | ([page]) => {
7 | const statusMatch = /id="page-status" value="([^"]+)"/.exec(page)
8 | if (statusMatch) {
9 | return {
10 | url: 'https://dict.eudic.net/Dicts/en/tab-detail/-12',
11 | method: 'post',
12 | transformResponse: [data => data],
13 | headers: {
14 | 'Content-Type': 'application/x-www-form-urlencoded'
15 | },
16 | responseType: 'text/plain',
17 | data: `status=${statusMatch[1]}`
18 | }
19 | }
20 | }
21 | ]
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/eudic/requests.mock.ts:
--------------------------------------------------------------------------------
1 | import { MockRequest } from '@/components/dictionaries/helpers'
2 |
3 | export const mockSearchTexts = ['love']
4 |
5 | export const mockRequest: MockRequest = mock => {
6 | mock.onAny(/eudic/).reply(info => {
7 | const file = /tab-detail/.test(info.url || '') ? 'sentences' : 'love'
8 | return [200, require(`raw-loader!./response/${file}.html`).default]
9 | })
10 | }
11 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/googledict/fixtures.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | files: [
3 | [
4 | 'chs-mouse.html',
5 | 'https://www.google.com/search?hl=en&safe=off&q=meaning:mouse'
6 | ],
7 | [
8 | 'chs-爱.html',
9 | 'https://www.google.com/search?hl=en&safe=off&q=meaning:' +
10 | encodeURIComponent('爱')
11 | ],
12 | [
13 | 'en-love.html',
14 | 'https://www.google.com/search?hl=en&safe=off&q=meaning:love'
15 | ],
16 | [
17 | 'en-salad.html',
18 | 'https://www.google.com/search?hl=en&safe=off&q=meaning:salad'
19 | ]
20 | ]
21 | }
22 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/googledict/requests.mock.ts:
--------------------------------------------------------------------------------
1 | import { MockRequest } from '@/components/dictionaries/helpers'
2 |
3 | export const mockSearchTexts = ['salad', 'mouse', '爱', 'love']
4 |
5 | export const mockRequest: MockRequest = mock => {
6 | mock
7 | .onGet(/google.+(define|meaning).+mouse/)
8 | .reply(200, require('!raw-loader!./response/chs-mouse.html').default)
9 |
10 | mock
11 | .onGet(new RegExp(encodeURIComponent('爱')))
12 | .reply(200, require('!raw-loader!./response/chs-爱.html').default)
13 |
14 | mock
15 | .onGet(/google.+(define|meaning).+love/)
16 | .reply(200, require('!raw-loader!./response/en-love.html').default)
17 |
18 | mock
19 | .onGet(/google.+(define|meaning).+salad/)
20 | .reply(200, require('!raw-loader!./response/en-salad.html').default)
21 | }
22 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/guoyu/engine.spec.ts:
--------------------------------------------------------------------------------
1 | import { retry } from '../helpers'
2 | import { search } from '@/components/dictionaries/guoyu/engine'
3 | import { getDefaultConfig } from '@/app-config'
4 | import { getDefaultProfile } from '@/app-config/profiles'
5 |
6 | describe('Dict/GuoYu/engine', () => {
7 | it('should parse result correctly', () => {
8 | return retry(() =>
9 | search('愛', getDefaultConfig(), getDefaultProfile(), {
10 | isPDF: false
11 | }).then(searchResult => {
12 | expect(searchResult.audio && typeof searchResult.audio.py).toBe(
13 | 'string'
14 | )
15 | expect(typeof searchResult.result.t).toBe('string')
16 | expect(Array.isArray(searchResult.result.h)).toBeTruthy()
17 | expect(searchResult.result.translation).toBeTruthy()
18 | })
19 | )
20 | })
21 | })
22 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/guoyu/fixtures.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | files: [
3 | ['愛.json', `https://www.moedict.tw/a/${encodeURIComponent('愛')}.json`]
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/guoyu/requests.mock.ts:
--------------------------------------------------------------------------------
1 | import { MockRequest } from '@/components/dictionaries/helpers'
2 |
3 | export const mockSearchTexts = ['愛']
4 |
5 | export const mockRequest: MockRequest = mock => {
6 | mock.onGet(/moedict/).reply(200, require('./response/愛.json'))
7 | }
8 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/helpers.ts:
--------------------------------------------------------------------------------
1 | import { timer } from '@/_helpers/promise-more'
2 |
3 | export async function retry(executor: () => Promise, retryTimes = 1) {
4 | let times = retryTimes + 1
5 | while (times--) {
6 | try {
7 | return await executor()
8 | } catch (e) {
9 | await timer(1000)
10 | }
11 | }
12 | console.error('>>>>> timeout')
13 | }
14 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/hjdict/fixtures.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | files: [
3 | ['henr.html', 'https://www.hjdict.com/w/henr'],
4 | ['love.html', 'https://www.hjdict.com/w/love'],
5 | ['爱.html', 'https://www.hjdict.com/jp/jc/%E7%88%B1']
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/hjdict/requests.mock.ts:
--------------------------------------------------------------------------------
1 | import { MockRequest } from '@/components/dictionaries/helpers'
2 |
3 | export const mockSearchTexts = ['爱', 'love', 'henr']
4 |
5 | export const mockRequest: MockRequest = mock => {
6 | mock.onGet(/hjdict/).reply(info => {
7 | const wordMatch = /[^/]+$/.exec(info.url || '')
8 | return wordMatch
9 | ? [
10 | 200,
11 | require(`raw-loader!./response/${decodeURIComponent(
12 | wordMatch[0]
13 | )}.html`).default
14 | ]
15 | : [404]
16 | })
17 | }
18 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/jikipedia/engine.spec.ts:
--------------------------------------------------------------------------------
1 | import { retry } from '../helpers'
2 | import { search } from '@/components/dictionaries/jikipedia/engine'
3 | import { getDefaultConfig } from '@/app-config'
4 | import { getDefaultProfile } from '@/app-config/profiles'
5 |
6 | describe('Dict/Jikipedia/engine', () => {
7 | it('should parse result correctly', () => {
8 | return retry(() =>
9 | search('xswl', getDefaultConfig(), getDefaultProfile(), {
10 | isPDF: false
11 | }).then(searchResult => {
12 | expect(typeof searchResult.result.length).toBeGreaterThan(0)
13 | expect(searchResult.result[0].title).toBe('string')
14 | expect(searchResult.result[0].content).toBe('string')
15 | })
16 | )
17 | })
18 | })
19 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/jikipedia/fixtures.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | files: [['xswl.html', 'https://jikipedia.com/search?phrase=xswl']]
3 | }
4 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/jikipedia/requests.mock.ts:
--------------------------------------------------------------------------------
1 | import { MockRequest } from '@/components/dictionaries/helpers'
2 |
3 | export const mockSearchTexts = ['xswl']
4 |
5 | export const mockRequest: MockRequest = mock => {
6 | mock
7 | .onGet(/jikipedia/)
8 | .reply(200, require(`raw-loader!./response/xswl.html`).default)
9 | }
10 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/jukuu/engine.spec.ts:
--------------------------------------------------------------------------------
1 | import { retry } from '../helpers'
2 | import { search } from '@/components/dictionaries/jukuu/engine'
3 | import { getDefaultConfig } from '@/app-config'
4 | import { getDefaultProfile } from '@/app-config/profiles'
5 |
6 | describe('Dict/Jukuu/engine', () => {
7 | it('should parse result correctly', () => {
8 | return retry(() =>
9 | search('love', getDefaultConfig(), getDefaultProfile(), {
10 | isPDF: false
11 | }).then(searchResult => {
12 | expect(typeof searchResult.result.lang).toBe('string')
13 | expect(searchResult.result.sens.length).toBeGreaterThan(0)
14 | expect(typeof searchResult.result.sens[0].trans).toBe('string')
15 | expect(searchResult.result.sens[0].trans.length).toBeGreaterThan(0)
16 | })
17 | )
18 | })
19 | })
20 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/jukuu/fixtures.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | files: [['love.html', 'http://www.jukuu.com/search.php?q=love']]
3 | }
4 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/jukuu/requests.mock.ts:
--------------------------------------------------------------------------------
1 | import { MockRequest } from '@/components/dictionaries/helpers'
2 |
3 | export const mockSearchTexts = ['love']
4 |
5 | export const mockRequest: MockRequest = mock => {
6 | mock
7 | .onGet(/jukuu/)
8 | .reply(200, require(`raw-loader!./response/love.html`).default)
9 | }
10 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/lexico/fixtures.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | files: [
3 | ['jumblish.html', 'https://www.lexico.com/definition/jumblish'],
4 | ['love.html', 'https://www.lexico.com/definition/love'],
5 | ['how.html', 'https://www.lexico.com/definition/how']
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/lexico/requests.mock.ts:
--------------------------------------------------------------------------------
1 | import { MockRequest } from '@/components/dictionaries/helpers'
2 |
3 | export const mockSearchTexts = ['how', 'love', 'jumblish']
4 |
5 | export const mockRequest: MockRequest = mock => {
6 | mock.onGet(/lexico/).reply(info => {
7 | const wordMatch = /[^/]+$/.exec(info.url || '')
8 | return wordMatch
9 | ? [
10 | 200,
11 | require(`raw-loader!./response/${decodeURIComponent(
12 | wordMatch[0]
13 | )}.html`).default
14 | ]
15 | : [404]
16 | })
17 | }
18 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/liangan/fixtures.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | files: [
3 | ['愛.json', `https://www.moedict.tw/c/${encodeURIComponent('愛')}.json`]
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/liangan/requests.mock.ts:
--------------------------------------------------------------------------------
1 | export { mockSearchTexts, mockRequest } from '../guoyu/requests.mock'
2 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/longman/fixtures.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | files: [
3 | ['jumblish.html', 'http://www.ldoceonline.com/dictionary/jumblish'],
4 | ['love.html', 'http://www.ldoceonline.com/dictionary/love'],
5 | ['profit.html', 'http://www.ldoceonline.com/dictionary/profit']
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/longman/requests.mock.ts:
--------------------------------------------------------------------------------
1 | import { MockRequest } from '@/components/dictionaries/helpers'
2 |
3 | export const mockSearchTexts = ['love', 'profit', 'jumblish']
4 |
5 | export const mockRequest: MockRequest = mock => {
6 | mock.onGet(/ldoceonline/).reply(info => {
7 | const wordMatch = /[^/]+$/.exec(info.url || '')
8 | return wordMatch
9 | ? [
10 | 200,
11 | require(`raw-loader!./response/${decodeURIComponent(
12 | wordMatch[0]
13 | )}.html`).default
14 | ]
15 | : [404]
16 | })
17 | }
18 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/macmillan/fixtures.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | files: [
3 | [
4 | 'jumblish.html',
5 | 'http://www.macmillandictionary.com/dictionary/british/jumblish'
6 | ],
7 | ['love.html', 'http://www.macmillandictionary.com/dictionary/british/love'],
8 | [
9 | 'love_2.html',
10 | 'http://www.macmillandictionary.com/dictionary/british/love_2'
11 | ],
12 | [
13 | 'viral.html',
14 | 'http://www.macmillandictionary.com/dictionary/british/viral'
15 | ]
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/macmillan/requests.mock.ts:
--------------------------------------------------------------------------------
1 | import { MockRequest } from '@/components/dictionaries/helpers'
2 |
3 | export const mockSearchTexts = ['love', 'viral', 'jumblish']
4 |
5 | export const mockRequest: MockRequest = mock => {
6 | mock.onGet(/macmillan/).reply(info => {
7 | const wordMatch = /[^/]+$/.exec(info.url || '')
8 | return wordMatch
9 | ? [
10 | 200,
11 | require(`raw-loader!./response/${decodeURIComponent(
12 | wordMatch[0]
13 | )}.html`).default
14 | ]
15 | : [404]
16 | })
17 | }
18 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/merriamwebster/fixtures.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | files: [
3 | ['love.html', 'https://www.merriam-webster.com/dictionary/love'],
4 | ['text.html', 'https://www.merriam-webster.com/dictionary/text'],
5 | ['salad.html', 'https://www.merriam-webster.com/dictionary/salad'],
6 | ['add.html', 'https://www.merriam-webster.com/dictionary/add'],
7 | ['transitive.html', 'https://www.merriam-webster.com/dictionary/transitive']
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/merriamwebster/requests.mock.ts:
--------------------------------------------------------------------------------
1 | import { MockRequest } from '@/components/dictionaries/helpers'
2 |
3 | export const mockSearchTexts = ['love', 'text', 'salad']
4 |
5 | export const mockRequest: MockRequest = mock => {
6 | mock
7 | .onGet(/merriam-webster.+love$/)
8 | .reply(200, require(`raw-loader!./response/love.html`).default)
9 |
10 | mock
11 | .onGet(/merriam-webster.+text$/)
12 | .reply(200, require(`raw-loader!./response/text.html`).default)
13 |
14 | mock
15 | .onGet(/merriam-webster.+salad$/)
16 | .reply(200, require(`raw-loader!./response/salad.html`).default)
17 | }
18 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/mojidict/requests.mock.ts:
--------------------------------------------------------------------------------
1 | import { MockRequest } from '@/components/dictionaries/helpers'
2 |
3 | export const mockSearchTexts = ['心']
4 |
5 | export const mockRequest: MockRequest = mock => {
6 | mock
7 | .onPost(/mojidict.*fetchWord/)
8 | .reply(200, require(`./response/心/fetchWord.json`))
9 | .onPost(/mojidict.*search/)
10 | .reply(200, require(`./response/心/search.json`))
11 | .onPost(/mojidict.*fetchTts/)
12 | .reply(200, require(`./response/心/fetchTts.json`))
13 | }
14 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/naver/fixtures.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | files: [
3 | [
4 | '愛.json',
5 | 'https://ja.dict.naver.com/api3/jako/search?query=' +
6 | encodeURIComponent('愛')
7 | ],
8 | [
9 | '爱.json',
10 | `https://zh.dict.naver.com/api3/zhko/search?query=${encodeURIComponent(
11 | '爱'
12 | )}&lang=zh_CN`
13 | ]
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/naver/requests.mock.ts:
--------------------------------------------------------------------------------
1 | import { MockRequest } from '@/components/dictionaries/helpers'
2 |
3 | export const mockSearchTexts = ['爱', '愛']
4 |
5 | export const mockRequest: MockRequest = mock => {
6 | mock
7 | .onGet(new RegExp('naver.+' + encodeURIComponent('爱')))
8 | .reply(200, require(`./response/爱.json`))
9 |
10 | mock
11 | .onGet(new RegExp('naver.+' + encodeURIComponent('愛')))
12 | .reply(200, require(`./response/愛.json`))
13 | }
14 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/oaldict/fixtures.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | files: [
3 | [
4 | 'comment.html',
5 | 'https://www.oxfordlearnersdictionaries.com/search/english/direct/?q=comment'
6 | ],
7 | [
8 | 'love.html',
9 | 'https://www.oxfordlearnersdictionaries.com/search/english/direct/?q=love'
10 | ],
11 | [
12 | 'salad.html',
13 | 'https://www.oxfordlearnersdictionaries.com/search/english/direct/?q=salad'
14 | ]
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/oaldict/requests.mock.ts:
--------------------------------------------------------------------------------
1 | import { MockRequest } from '@/components/dictionaries/helpers'
2 |
3 | export const mockSearchTexts = ['comment', 'love', 'salad']
4 |
5 | export const mockRequest: MockRequest = mock => {
6 | mock
7 | .onGet(/oxfordlearnersdictionaries.+comment$/)
8 | .reply(200, require('!raw-loader!./response/comment.html').default)
9 |
10 | mock
11 | .onGet(/oxfordlearnersdictionaries.+love$/)
12 | .reply(200, require('!raw-loader!./response/love.html').default)
13 |
14 | mock
15 | .onGet(/oxfordlearnersdictionaries.+salad$/)
16 | .reply(200, require('!raw-loader!./response/salad.html').default)
17 | }
18 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/renren/engine.spec.ts:
--------------------------------------------------------------------------------
1 | import { retry } from '../helpers'
2 | import { search } from '@/components/dictionaries/renren/engine'
3 | import { getDefaultConfig } from '@/app-config'
4 | import { getDefaultProfile } from '@/app-config/profiles'
5 |
6 | describe('Dict/Renren/engine', () => {
7 | it('should parse result correctly', () => {
8 | return retry(() =>
9 | search('love', getDefaultConfig(), getDefaultProfile(), {
10 | isPDF: false
11 | }).then(searchResult => {})
12 | )
13 | })
14 | })
15 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/renren/fixtures.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | files: [
3 | ['love.html', 'https://www.91dict.com/words?w=love'],
4 | [
5 | 'detail.html',
6 | ([page]) => {
7 | const detailMatch = /\/r_subs\?sub_id=[^"']+/.exec(page)
8 | if (detailMatch) {
9 | return {
10 | url: 'https://www.91dict.com' + detailMatch
11 | }
12 | }
13 | }
14 | ]
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/renren/requests.mock.ts:
--------------------------------------------------------------------------------
1 | import { MockRequest } from '@/components/dictionaries/helpers'
2 |
3 | export const mockSearchTexts = ['love']
4 |
5 | export const mockRequest: MockRequest = mock => {
6 | mock
7 | .onGet(/91dict.+r_subs/)
8 | .reply(200, require(`raw-loader!./response/detail.html`).default)
9 |
10 | mock
11 | .onGet(/91dict/)
12 | .reply(200, require(`raw-loader!./response/love.html`).default)
13 | }
14 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/shanbay/fixtures.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | files: [
3 | ['love.html', 'https://www.shanbay.com/bdc/mobile/preview/word?word=love'],
4 | [
5 | 'love.json',
6 | ([page]) => {
7 | const wordIdMatch = /"word-spell" data-id="([^"]+)"/.exec(page)
8 | if (wordIdMatch) {
9 | return {
10 | url: `https://www.shanbay.com/api/v1/bdc/example/?vocabulary_id=${
11 | wordIdMatch[1]
12 | }&type=sys`
13 | }
14 | }
15 | }
16 | ]
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/shanbay/requests.mock.ts:
--------------------------------------------------------------------------------
1 | import { MockRequest } from '@/components/dictionaries/helpers'
2 |
3 | export const mockSearchTexts = ['love']
4 |
5 | export const mockRequest: MockRequest = mock => {
6 | mock.onGet(/shanbay/).reply(info => {
7 | return /mobile/.test(info.url || '')
8 | ? [200, require(`raw-loader!./response/love.html`).default]
9 | : [200, require(`./response/love.json`)]
10 | })
11 | }
12 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/urban/fixtures.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | files: [['love.html', 'http://www.urbandictionary.com/define.php?term=love']]
3 | }
4 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/urban/requests.mock.ts:
--------------------------------------------------------------------------------
1 | import { MockRequest } from '@/components/dictionaries/helpers'
2 |
3 | export const mockSearchTexts = ['love']
4 |
5 | export const mockRequest: MockRequest = mock => {
6 | mock
7 | .onGet(/urbandictionary/)
8 | .reply(200, require(`raw-loader!./response/love.html`).default)
9 | }
10 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/vocabulary/engine.spec.ts:
--------------------------------------------------------------------------------
1 | import { retry } from '../helpers'
2 | import { search } from '@/components/dictionaries/vocabulary/engine'
3 | import { getDefaultConfig } from '@/app-config'
4 | import { getDefaultProfile } from '@/app-config/profiles'
5 |
6 | describe('Dict/Vocabulary/engine', () => {
7 | it('should parse result correctly', () => {
8 | return retry(() =>
9 | search('love', getDefaultConfig(), getDefaultProfile(), {
10 | isPDF: false
11 | }).then(({ result, audio }) => {
12 | expect(audio).toBeUndefined()
13 | expect(typeof result.long).toBe('string')
14 | expect(typeof result.short).toBe('string')
15 | })
16 | )
17 | })
18 | })
19 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/vocabulary/fixtures.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | files: [['love.html', 'https://www.vocabulary.com/dictionary/love']]
3 | }
4 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/vocabulary/requests.mock.ts:
--------------------------------------------------------------------------------
1 | import { MockRequest } from '@/components/dictionaries/helpers'
2 |
3 | export const mockSearchTexts = ['love']
4 |
5 | export const mockRequest: MockRequest = mock => {
6 | mock
7 | .onGet(/vocabulary/)
8 | .reply(200, require(`raw-loader!./response/love.html`).default)
9 | }
10 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/weblio/engine.spec.ts:
--------------------------------------------------------------------------------
1 | import { retry } from '../helpers'
2 | import { search } from '@/components/dictionaries/weblio/engine'
3 | import { getDefaultConfig } from '@/app-config'
4 | import { getDefaultProfile } from '@/app-config/profiles'
5 |
6 | describe('Dict/Weblio/engine', () => {
7 | ;['love', '吐く', '当たる'].forEach(text => {
8 | it(`should parse result ${text} correctly`, () => {
9 | return retry(() =>
10 | search(text, getDefaultConfig(), getDefaultProfile(), {
11 | isPDF: false
12 | }).then(({ result }) => {
13 | expect(result.length).toBeGreaterThanOrEqual(1)
14 | expect(typeof result[0].title).toBe('string')
15 | expect(typeof result[0].def).toBe('string')
16 | })
17 | )
18 | })
19 | })
20 | })
21 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/weblio/fixtures.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | files: [
3 | [
4 | '主催.html',
5 | 'https://www.weblio.jp/content/' + encodeURIComponent('主催')
6 | ],
7 | ['love.html', 'https://www.weblio.jp/content/love'],
8 | [
9 | '吐く.html',
10 | 'https://www.weblio.jp/content/' + encodeURIComponent('吐く')
11 | ],
12 | [
13 | '当たる.html',
14 | 'https://www.weblio.jp/content/' + encodeURIComponent('当たる')
15 | ]
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/weblio/requests.mock.ts:
--------------------------------------------------------------------------------
1 | import { MockRequest } from '@/components/dictionaries/helpers'
2 |
3 | export const mockSearchTexts = ['主催', 'love', '吐く', '当たる']
4 |
5 | export const mockRequest: MockRequest = mock => {
6 | mock
7 | .onGet(/www\.weblio\.jp.+love/)
8 | .reply(200, require(`raw-loader!./response/love.html`).default)
9 |
10 | mock
11 | .onGet(new RegExp('www\\.weblio\\.jp.+' + encodeURIComponent('吐く')))
12 | .reply(200, require(`raw-loader!./response/吐く.html`).default)
13 |
14 | mock
15 | .onGet(new RegExp('www\\.weblio\\.jp.+' + encodeURIComponent('当たる')))
16 | .reply(200, require(`raw-loader!./response/当たる.html`).default)
17 |
18 | mock
19 | .onGet(new RegExp('www\\.weblio\\.jp.+' + encodeURIComponent('主催')))
20 | .reply(200, require(`raw-loader!./response/主催.html`).default)
21 | }
22 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/weblioejje/engine.spec.ts:
--------------------------------------------------------------------------------
1 | import { retry } from '../helpers'
2 | import { search } from '@/components/dictionaries/weblioejje/engine'
3 | import { getDefaultConfig } from '@/app-config'
4 | import { getDefaultProfile } from '@/app-config/profiles'
5 |
6 | describe('Dict/Weblioejje/engine', () => {
7 | ;['love', '愛'].forEach(text => {
8 | it(`should parse result ${text} correctly`, () => {
9 | return retry(() =>
10 | search(text, getDefaultConfig(), getDefaultProfile(), {
11 | isPDF: false
12 | }).then(({ result }) => {
13 | expect(result.length).toBeGreaterThanOrEqual(1)
14 | for (const { content } of result) {
15 | expect(typeof content).toBe('string')
16 | expect(content.length).toBeGreaterThan(1)
17 | }
18 | })
19 | )
20 | })
21 | })
22 | })
23 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/weblioejje/fixtures.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | files: [
3 | ['love.html', 'https://ejje.weblio.jp/content/love'],
4 | ['愛.html', 'https://ejje.weblio.jp/content/' + encodeURIComponent('愛')]
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/weblioejje/requests.mock.ts:
--------------------------------------------------------------------------------
1 | import { MockRequest } from '@/components/dictionaries/helpers'
2 |
3 | export const mockSearchTexts = ['love', '愛']
4 |
5 | export const mockRequest: MockRequest = mock => {
6 | mock
7 | .onGet(/ejje\.weblio\.jp.+love/)
8 | .reply(200, require(`raw-loader!./response/love.html`).default)
9 |
10 | mock
11 | .onGet(new RegExp('ejje\\.weblio\\.jp.+' + encodeURIComponent('愛')))
12 | .reply(200, require(`raw-loader!./response/愛.html`).default)
13 | }
14 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/websterlearner/fixtures.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | files: [
3 | ['door.html', 'http://www.learnersdictionary.com/definition/door'],
4 | ['house.html', 'http://www.learnersdictionary.com/definition/house'],
5 | ['jumblish.html', 'http://www.learnersdictionary.com/definition/jumblish']
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/websterlearner/requests.mock.ts:
--------------------------------------------------------------------------------
1 | import { MockRequest } from '@/components/dictionaries/helpers'
2 |
3 | export const mockSearchTexts = ['door', 'house', 'jumblish']
4 |
5 | export const mockRequest: MockRequest = mock => {
6 | mock.onGet(/learnersdictionary/).reply(info => {
7 | const wordMatch = /[^/]+$/.exec(info.url || '')
8 | return wordMatch
9 | ? [
10 | 200,
11 | require(`raw-loader!./response/${decodeURIComponent(
12 | wordMatch[0]
13 | )}.html`).default
14 | ]
15 | : [404]
16 | })
17 | }
18 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/wikipedia/engine.spec.ts:
--------------------------------------------------------------------------------
1 | import { retry } from '../helpers'
2 | import { search } from '@/components/dictionaries/wikipedia/engine'
3 | import { getDefaultConfig } from '@/app-config'
4 | import { getDefaultProfile } from '@/app-config/profiles'
5 |
6 | describe('Dict/Wikipedia/engine', () => {
7 | it('should parse result correctly', () => {
8 | return retry(() =>
9 | search('数字', getDefaultConfig(), getDefaultProfile(), {
10 | isPDF: false
11 | }).then(({ result }) => {
12 | expect(typeof result.title).toBe('string')
13 | expect(typeof result.content).toBe('string')
14 | })
15 | )
16 | })
17 | })
18 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/wikipedia/fixtures.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | files: [
3 | [
4 | 'langlist.html',
5 | 'https://zh.m.wikipedia.org/wiki/Special:%E7%A7%BB%E5%8A%A8%E7%89%88%E8%AF%AD%E8%A8%80/%E6%95%B8%E5%AD%97'
6 | ],
7 | ['数字.html', 'https://zh.m.wikipedia.org/wiki/%E6%95%B0%E5%AD%97']
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/wikipedia/requests.mock.ts:
--------------------------------------------------------------------------------
1 | import { MockRequest } from '@/components/dictionaries/helpers'
2 |
3 | export const mockSearchTexts = ['数字']
4 |
5 | export const mockRequest: MockRequest = mock => {
6 | mock
7 | .onGet(/wikipedia.+Special/)
8 | .reply(200, require(`raw-loader!./response/langlist.html`).default)
9 |
10 | mock
11 | .onGet(/m\.wikipedia/)
12 | .reply(200, require(`raw-loader!./response/数字.html`).default)
13 | }
14 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/youdao/fixtures.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | files: [
3 | ['jumblish.html', 'https://dict.youdao.com/w/jumblish'],
4 | ['love.html', 'https://dict.youdao.com/w/love'],
5 | ['make.html', 'https://dict.youdao.com/w/make'], // collins
6 | [
7 | 'translation.html',
8 | 'https://dict.youdao.com/w/' +
9 | encodeURIComponent(
10 | `She walks in beauty, like the night Of cloudless climes and starry skies.`
11 | )
12 | ]
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/youdao/requests.mock.ts:
--------------------------------------------------------------------------------
1 | import { MockRequest } from '@/components/dictionaries/helpers'
2 |
3 | export const mockSearchTexts = ['make', 'love', 'translation', 'jumblish']
4 |
5 | export const mockRequest: MockRequest = mock => {
6 | mock.onGet(/youdao/).reply(info => {
7 | const wordMatch = /[^/]+$/.exec(info.url || '')
8 | return wordMatch
9 | ? [
10 | 200,
11 | require(`raw-loader!./response/${decodeURIComponent(
12 | wordMatch[0]
13 | )}.html`).default
14 | ]
15 | : [404]
16 | })
17 | }
18 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/zdic/fixtures.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | files: [
3 | ['沙拉.html', 'https://www.zdic.net/hans/' + encodeURIComponent('沙拉')],
4 | ['爱.html', 'https://www.zdic.net/hans/' + encodeURIComponent('爱')]
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/test/specs/components/dictionaries/zdic/requests.mock.ts:
--------------------------------------------------------------------------------
1 | import { MockRequest } from '@/components/dictionaries/helpers'
2 |
3 | export const mockSearchTexts = ['沙拉', '爱']
4 |
5 | export const mockRequest: MockRequest = mock => {
6 | mock.onGet(/zdic/).reply(info => {
7 | const wordMatch = /[^/]+$/.exec(info.url || '')
8 | return wordMatch
9 | ? [
10 | 200,
11 | require(`raw-loader!./response/${decodeURIComponent(
12 | wordMatch[0]
13 | )}.html`).default
14 | ]
15 | : [404]
16 | })
17 | }
18 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | // Target latest version of ECMAScript.
4 | "target": "esnext",
5 | // Search under node_modules for non-relative imports.
6 | "moduleResolution": "node",
7 | // Process & infer types from .js files.
8 | "allowJs": true,
9 | // Don't emit; allow Babel to transform files.
10 | "noEmit": true,
11 | // Enable strictest settings like strictNullChecks & noImplicitAny.
12 | "strict": true,
13 | // Disallow features that require cross-file information for emit.
14 | "isolatedModules": true,
15 | // Import non-ES modules as default imports.
16 | "esModuleInterop": true,
17 | "module": "esnext",
18 | "resolveJsonModule": true,
19 | "noImplicitAny": false,
20 | "jsx": "react",
21 | "baseUrl": "./",
22 | "paths": {
23 | "@/*": ["src/*"]
24 | },
25 | "typeRoots": ["node_modules/@types", "src/typings"],
26 | "lib": ["esnext", "dom", "dom.iterable"]
27 | },
28 | "include": ["src", "test", ".storybook"]
29 | }
30 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | module.exports = require('neutrino')().webpack()
2 |
--------------------------------------------------------------------------------