├── .node-version ├── .npmrc ├── .prettierignore ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── feature_request.md │ └── bug_report.md ├── workflows │ └── nodejs.yml ├── issue_template.md └── actions │ └── shared-build │ └── action.yml ├── assets ├── data │ ├── theme.json │ ├── fcd │ │ ├── meta.json │ │ └── shiptag.json │ ├── mirror.json │ └── server.json ├── img │ ├── logo.png │ ├── header.png │ ├── material │ │ ├── 01.png │ │ ├── 02.png │ │ ├── 03.png │ │ ├── 04.png │ │ ├── 05.png │ │ ├── 06.png │ │ ├── 07.png │ │ ├── 08.png │ │ ├── 1.png │ │ ├── 10.png │ │ ├── 11.png │ │ ├── 12.png │ │ ├── 2.png │ │ ├── 3.png │ │ ├── 4.png │ │ ├── 5.png │ │ ├── 6.png │ │ ├── 7.png │ │ ├── 8.png │ │ ├── 010.png │ │ ├── 011.png │ │ ├── 012.png │ │ ├── 1_big.png │ │ ├── 2_big.png │ │ ├── 3_big.png │ │ ├── 4_big.png │ │ ├── 5_big.png │ │ ├── 6_big.png │ │ ├── 7_big.png │ │ └── 8_big.png │ ├── slotitem │ │ ├── -1.png │ │ ├── 0.png │ │ ├── 1.png │ │ ├── 10.png │ │ ├── 11.png │ │ ├── 12.png │ │ ├── 13.png │ │ ├── 14.png │ │ ├── 15.png │ │ ├── 16.png │ │ ├── 17.png │ │ ├── 18.png │ │ ├── 19.png │ │ ├── 2.png │ │ ├── 20.png │ │ ├── 21.png │ │ ├── 22.png │ │ ├── 23.png │ │ ├── 24.png │ │ ├── 25.png │ │ ├── 26.png │ │ ├── 27.png │ │ ├── 28.png │ │ ├── 29.png │ │ ├── 3.png │ │ ├── 30.png │ │ ├── 31.png │ │ ├── 32.png │ │ ├── 33.png │ │ ├── 34.png │ │ ├── 35.png │ │ ├── 36.png │ │ ├── 37.png │ │ ├── 38.png │ │ ├── 39.png │ │ ├── 4.png │ │ ├── 40.png │ │ ├── 41.png │ │ ├── 42.png │ │ ├── 43.png │ │ ├── 44.png │ │ ├── 45.png │ │ ├── 46.png │ │ ├── 47.png │ │ ├── 5.png │ │ ├── 6.png │ │ ├── 7.png │ │ ├── 8.png │ │ ├── 9.png │ │ ├── 100.png │ │ ├── 101.png │ │ ├── 102.png │ │ ├── 103.png │ │ ├── 104.png │ │ ├── 105.png │ │ ├── 106.png │ │ ├── 107.png │ │ ├── 108.png │ │ ├── 109.png │ │ ├── 110.png │ │ ├── 111.png │ │ ├── 112.png │ │ ├── 113.png │ │ ├── 114.png │ │ ├── 115.png │ │ ├── 116.png │ │ ├── 117.png │ │ ├── 118.png │ │ ├── 119.png │ │ ├── 120.png │ │ ├── 121.png │ │ ├── 122.png │ │ ├── 123.png │ │ ├── 124.png │ │ ├── 125.png │ │ ├── 126.png │ │ ├── 127.png │ │ ├── 128.png │ │ ├── 129.png │ │ ├── 130.png │ │ ├── 131.png │ │ ├── 132.png │ │ ├── 133.png │ │ ├── 134.png │ │ ├── 135.png │ │ ├── 136.png │ │ ├── 137.png │ │ ├── 138.png │ │ ├── 139.png │ │ ├── 140.png │ │ ├── 141.png │ │ ├── 142.png │ │ ├── 143.png │ │ ├── 144.png │ │ ├── 145.png │ │ ├── 146.png │ │ ├── 147.png │ │ ├── 148.png │ │ ├── 149.png │ │ ├── 150.png │ │ ├── 151.png │ │ ├── 152.png │ │ ├── 154.png │ │ ├── 155.png │ │ ├── 156.png │ │ ├── 157.png │ │ └── 158.png │ ├── airplane │ │ ├── alv1.png │ │ ├── alv2.png │ │ ├── alv3.png │ │ ├── alv4.png │ │ ├── alv5.png │ │ ├── alv6.png │ │ └── alv7.png │ ├── touchbar │ │ ├── lock.png │ │ ├── media.png │ │ ├── camera.png │ │ ├── console.png │ │ ├── refresh.png │ │ ├── unlock.png │ │ ├── angle-left.png │ │ ├── fullscreen.png │ │ ├── volume-off.png │ │ ├── volume-up.png │ │ ├── angle-right.png │ │ └── social-media.png │ └── operation │ │ ├── build.png │ │ ├── repair.png │ │ ├── sortie.png │ │ └── expedition.png ├── audio │ ├── fail.mp3 │ ├── poi.mp3 │ ├── about.mp3 │ └── update.mp3 ├── icons │ ├── icon.png │ ├── poi.icns │ ├── poi.ico │ ├── poi.png │ ├── poi_32x32.png │ ├── poi_36x36.png │ ├── icon_16x16.png │ ├── poi_128x128.png │ ├── poi_256x256.png │ ├── poi_512x512.png │ ├── poi_1024x1024.png │ ├── poi_ribbon_dark.png │ ├── poi_ribbon_light.png │ ├── poi_ribbon_dark@2x.png │ ├── poi_ribbon_dark@4x.png │ ├── poi_ribbon_light@2x.png │ ├── poi_ribbon_light@4x.png │ ├── poi_ribbon_dark_256x256.png │ └── poi_ribbon_light_256x256.png ├── js │ ├── webview-window-preload.js │ ├── disable-tab.js │ ├── webview-preload.js │ ├── plugin-preload.js │ ├── capture-page.js │ └── page-align.js └── svg │ ├── COPYRIGHT.md │ └── slotitem │ ├── 0.svg │ ├── 1.svg │ ├── 20.svg │ ├── 52.svg │ ├── 2.svg │ ├── 3.svg │ ├── 16.svg │ ├── 26.svg │ ├── 24.svg │ └── 34.svg ├── views ├── battle-env.es ├── components │ ├── main │ │ └── assets │ │ │ └── img │ │ │ └── state │ │ │ ├── 0.png │ │ │ ├── 1.png │ │ │ ├── 2.png │ │ │ ├── 3.png │ │ │ └── 4.png │ ├── etc │ │ ├── window-env.ts │ │ ├── styled-components.ts │ │ ├── custom-tag.tsx │ │ ├── resize-sensor.tsx │ │ ├── context-button-tooltip.tsx │ │ ├── overlay.tsx │ │ └── webview-util.ts │ ├── settings │ │ ├── gaming │ │ │ ├── index.es │ │ │ └── pre-sortie-config │ │ │ │ ├── index.es │ │ │ │ ├── unused-slot-check-config.es │ │ │ │ └── slot-check-config.es │ │ ├── main │ │ │ ├── index.es │ │ │ ├── language-config.es │ │ │ ├── accessibility-config.es │ │ │ ├── advanced-config │ │ │ │ └── limit-fps.es │ │ │ └── screenshot-config.es │ │ ├── components │ │ │ ├── integer.es │ │ │ ├── switch.es │ │ │ ├── radio.es │ │ │ ├── section.tsx │ │ │ ├── checkbox.es │ │ │ └── text.es │ │ ├── about │ │ │ ├── contributors.es │ │ │ ├── index.es │ │ │ ├── thanks-to.es │ │ │ ├── gpu-status.es │ │ │ └── open-collective.es │ │ ├── display │ │ │ ├── zooming-config.es │ │ │ └── index.es │ │ ├── plugin │ │ │ └── plugin-setting-wrapper.es │ │ └── network │ │ │ └── connection-test.tsx │ ├── ship │ │ ├── oasw-indicator.es │ │ ├── slotitems-data.es │ │ └── aapb-indicator.es │ └── ship-parts │ │ └── statuslabel.es ├── create-store.ts ├── redux │ ├── timers │ │ └── index.es │ ├── wctf.es │ ├── info │ │ ├── __tests__ │ │ │ ├── __fixtures__ │ │ │ │ ├── api_req_air_corps_change_name.json │ │ │ │ ├── api_req_air_corps_set_action.json │ │ │ │ ├── api_req_air_corps_supply.json │ │ │ │ └── api_req_air_corps_set_plane.json │ │ │ └── quests.spec.es │ │ ├── server.es │ │ ├── index.es │ │ ├── constructions.ts │ │ ├── maps.es │ │ └── basic.ts │ ├── misc.es │ ├── ui.es │ ├── config │ │ └── index.ts │ ├── ipc.es │ ├── fcd.es │ ├── const.es │ └── plugins │ │ └── index.es ├── env-parts │ ├── dbg.ts │ ├── config.ts │ ├── modal.ts │ └── devtool-message.es ├── utils │ ├── color.ts │ ├── notifiers.es │ ├── event-emitter.ts │ ├── __tests__ │ │ └── aaci.spec.es │ └── file-writer.ts ├── services │ ├── worker.es │ ├── resource-notifier.es │ ├── google-analytics.es │ ├── sortie-dangerous-check.es │ ├── alert.ts │ ├── device-pixel-ratio-detector.es │ ├── sortie-expedition-resupply-check.es │ ├── sortie-unused-slot-check.es │ ├── sortie-free-slot-check.es │ └── development-prophecy.es ├── polyfills │ ├── react-fontawesome.es │ └── react-i18next.es └── theme.ts ├── poi.provisionprofile ├── .husky └── pre-commit ├── index-plugin.html ├── .stylelintignore ├── prettier.config.js ├── shims ├── vendor │ ├── header-case-normalizer.d.ts │ └── socks5-client.d.ts └── global.d.ts ├── lib ├── constant.ts ├── __mocks__ │ ├── fs-extra.es │ └── cson.es ├── devtool.ts ├── native-theme-helper.ts ├── screenshot.ts ├── updater.ts ├── shortcut.ts ├── __tests__ │ ├── __snapshots__ │ │ └── config.spec.es.snap │ └── config.spec.es ├── tray.ts ├── module-path.ts └── sentry.ts ├── babel-register.config.js ├── index.js ├── content ├── description │ ├── zh-CN.md │ ├── zh-TW.md │ ├── ja-JP.md │ └── en-US.md ├── welcome │ ├── ja-JP.md │ ├── zh-TW.md │ ├── zh-CN.md │ └── en-US.md ├── certerror │ ├── zh-CN.md │ ├── zh-TW.md │ ├── ja-JP.md │ └── en-US.md └── merge-content.js ├── .gitattributes ├── .mention-bot ├── lint-staged.config.js ├── setup-tests.ts ├── .vscode └── settings.json ├── i18n ├── data │ ├── ko-KR.json │ ├── ja-JP.json │ ├── en-US.json │ ├── zh-CN.json │ └── zh-TW.json └── menu │ ├── en-US.json │ ├── zh-CN.json │ ├── zh-TW.json │ ├── ko-KR.json │ └── ja-JP.json ├── tsconfig.json ├── jest.config.js ├── fcd └── README.md ├── .stylelintrc.js ├── .gitignore ├── babel.config.js ├── LICENSE ├── index.html ├── gulpfile.js └── electron-builder.config.js /.node-version: -------------------------------------------------------------------------------- 1 | 22 2 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | legacy-peer-deps=true 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | views/utils/aaci.es -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | open_collective: poi 2 | -------------------------------------------------------------------------------- /assets/data/theme.json: -------------------------------------------------------------------------------- 1 | [ 2 | "light", 3 | "dark" 4 | ] 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | -------------------------------------------------------------------------------- /views/battle-env.es: -------------------------------------------------------------------------------- 1 | // Compatibility 2 | import './create-store' 3 | -------------------------------------------------------------------------------- /assets/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/logo.png -------------------------------------------------------------------------------- /assets/audio/fail.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/audio/fail.mp3 -------------------------------------------------------------------------------- /assets/audio/poi.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/audio/poi.mp3 -------------------------------------------------------------------------------- /assets/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/icons/icon.png -------------------------------------------------------------------------------- /assets/icons/poi.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/icons/poi.icns -------------------------------------------------------------------------------- /assets/icons/poi.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/icons/poi.ico -------------------------------------------------------------------------------- /assets/icons/poi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/icons/poi.png -------------------------------------------------------------------------------- /assets/img/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/header.png -------------------------------------------------------------------------------- /poi.provisionprofile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/poi.provisionprofile -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /assets/audio/about.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/audio/about.mp3 -------------------------------------------------------------------------------- /assets/audio/update.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/audio/update.mp3 -------------------------------------------------------------------------------- /assets/icons/poi_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/icons/poi_32x32.png -------------------------------------------------------------------------------- /assets/icons/poi_36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/icons/poi_36x36.png -------------------------------------------------------------------------------- /assets/img/material/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/material/01.png -------------------------------------------------------------------------------- /assets/img/material/02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/material/02.png -------------------------------------------------------------------------------- /assets/img/material/03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/material/03.png -------------------------------------------------------------------------------- /assets/img/material/04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/material/04.png -------------------------------------------------------------------------------- /assets/img/material/05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/material/05.png -------------------------------------------------------------------------------- /assets/img/material/06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/material/06.png -------------------------------------------------------------------------------- /assets/img/material/07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/material/07.png -------------------------------------------------------------------------------- /assets/img/material/08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/material/08.png -------------------------------------------------------------------------------- /assets/img/material/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/material/1.png -------------------------------------------------------------------------------- /assets/img/material/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/material/10.png -------------------------------------------------------------------------------- /assets/img/material/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/material/11.png -------------------------------------------------------------------------------- /assets/img/material/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/material/12.png -------------------------------------------------------------------------------- /assets/img/material/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/material/2.png -------------------------------------------------------------------------------- /assets/img/material/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/material/3.png -------------------------------------------------------------------------------- /assets/img/material/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/material/4.png -------------------------------------------------------------------------------- /assets/img/material/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/material/5.png -------------------------------------------------------------------------------- /assets/img/material/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/material/6.png -------------------------------------------------------------------------------- /assets/img/material/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/material/7.png -------------------------------------------------------------------------------- /assets/img/material/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/material/8.png -------------------------------------------------------------------------------- /assets/img/slotitem/-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/-1.png -------------------------------------------------------------------------------- /assets/img/slotitem/0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/0.png -------------------------------------------------------------------------------- /assets/img/slotitem/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/1.png -------------------------------------------------------------------------------- /assets/img/slotitem/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/10.png -------------------------------------------------------------------------------- /assets/img/slotitem/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/11.png -------------------------------------------------------------------------------- /assets/img/slotitem/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/12.png -------------------------------------------------------------------------------- /assets/img/slotitem/13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/13.png -------------------------------------------------------------------------------- /assets/img/slotitem/14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/14.png -------------------------------------------------------------------------------- /assets/img/slotitem/15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/15.png -------------------------------------------------------------------------------- /assets/img/slotitem/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/16.png -------------------------------------------------------------------------------- /assets/img/slotitem/17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/17.png -------------------------------------------------------------------------------- /assets/img/slotitem/18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/18.png -------------------------------------------------------------------------------- /assets/img/slotitem/19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/19.png -------------------------------------------------------------------------------- /assets/img/slotitem/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/2.png -------------------------------------------------------------------------------- /assets/img/slotitem/20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/20.png -------------------------------------------------------------------------------- /assets/img/slotitem/21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/21.png -------------------------------------------------------------------------------- /assets/img/slotitem/22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/22.png -------------------------------------------------------------------------------- /assets/img/slotitem/23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/23.png -------------------------------------------------------------------------------- /assets/img/slotitem/24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/24.png -------------------------------------------------------------------------------- /assets/img/slotitem/25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/25.png -------------------------------------------------------------------------------- /assets/img/slotitem/26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/26.png -------------------------------------------------------------------------------- /assets/img/slotitem/27.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/27.png -------------------------------------------------------------------------------- /assets/img/slotitem/28.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/28.png -------------------------------------------------------------------------------- /assets/img/slotitem/29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/29.png -------------------------------------------------------------------------------- /assets/img/slotitem/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/3.png -------------------------------------------------------------------------------- /assets/img/slotitem/30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/30.png -------------------------------------------------------------------------------- /assets/img/slotitem/31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/31.png -------------------------------------------------------------------------------- /assets/img/slotitem/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/32.png -------------------------------------------------------------------------------- /assets/img/slotitem/33.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/33.png -------------------------------------------------------------------------------- /assets/img/slotitem/34.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/34.png -------------------------------------------------------------------------------- /assets/img/slotitem/35.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/35.png -------------------------------------------------------------------------------- /assets/img/slotitem/36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/36.png -------------------------------------------------------------------------------- /assets/img/slotitem/37.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/37.png -------------------------------------------------------------------------------- /assets/img/slotitem/38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/38.png -------------------------------------------------------------------------------- /assets/img/slotitem/39.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/39.png -------------------------------------------------------------------------------- /assets/img/slotitem/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/4.png -------------------------------------------------------------------------------- /assets/img/slotitem/40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/40.png -------------------------------------------------------------------------------- /assets/img/slotitem/41.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/41.png -------------------------------------------------------------------------------- /assets/img/slotitem/42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/42.png -------------------------------------------------------------------------------- /assets/img/slotitem/43.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/43.png -------------------------------------------------------------------------------- /assets/img/slotitem/44.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/44.png -------------------------------------------------------------------------------- /assets/img/slotitem/45.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/45.png -------------------------------------------------------------------------------- /assets/img/slotitem/46.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/46.png -------------------------------------------------------------------------------- /assets/img/slotitem/47.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/47.png -------------------------------------------------------------------------------- /assets/img/slotitem/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/5.png -------------------------------------------------------------------------------- /assets/img/slotitem/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/6.png -------------------------------------------------------------------------------- /assets/img/slotitem/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/7.png -------------------------------------------------------------------------------- /assets/img/slotitem/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/8.png -------------------------------------------------------------------------------- /assets/img/slotitem/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/9.png -------------------------------------------------------------------------------- /assets/js/webview-window-preload.js: -------------------------------------------------------------------------------- 1 | window.onbeforeunload = (e) => (e.returnValue = false) 2 | -------------------------------------------------------------------------------- /assets/icons/icon_16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/icons/icon_16x16.png -------------------------------------------------------------------------------- /assets/icons/poi_128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/icons/poi_128x128.png -------------------------------------------------------------------------------- /assets/icons/poi_256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/icons/poi_256x256.png -------------------------------------------------------------------------------- /assets/icons/poi_512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/icons/poi_512x512.png -------------------------------------------------------------------------------- /assets/img/airplane/alv1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/airplane/alv1.png -------------------------------------------------------------------------------- /assets/img/airplane/alv2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/airplane/alv2.png -------------------------------------------------------------------------------- /assets/img/airplane/alv3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/airplane/alv3.png -------------------------------------------------------------------------------- /assets/img/airplane/alv4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/airplane/alv4.png -------------------------------------------------------------------------------- /assets/img/airplane/alv5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/airplane/alv5.png -------------------------------------------------------------------------------- /assets/img/airplane/alv6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/airplane/alv6.png -------------------------------------------------------------------------------- /assets/img/airplane/alv7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/airplane/alv7.png -------------------------------------------------------------------------------- /assets/img/material/010.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/material/010.png -------------------------------------------------------------------------------- /assets/img/material/011.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/material/011.png -------------------------------------------------------------------------------- /assets/img/material/012.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/material/012.png -------------------------------------------------------------------------------- /assets/img/material/1_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/material/1_big.png -------------------------------------------------------------------------------- /assets/img/material/2_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/material/2_big.png -------------------------------------------------------------------------------- /assets/img/material/3_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/material/3_big.png -------------------------------------------------------------------------------- /assets/img/material/4_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/material/4_big.png -------------------------------------------------------------------------------- /assets/img/material/5_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/material/5_big.png -------------------------------------------------------------------------------- /assets/img/material/6_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/material/6_big.png -------------------------------------------------------------------------------- /assets/img/material/7_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/material/7_big.png -------------------------------------------------------------------------------- /assets/img/material/8_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/material/8_big.png -------------------------------------------------------------------------------- /assets/img/slotitem/100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/100.png -------------------------------------------------------------------------------- /assets/img/slotitem/101.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/101.png -------------------------------------------------------------------------------- /assets/img/slotitem/102.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/102.png -------------------------------------------------------------------------------- /assets/img/slotitem/103.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/103.png -------------------------------------------------------------------------------- /assets/img/slotitem/104.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/104.png -------------------------------------------------------------------------------- /assets/img/slotitem/105.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/105.png -------------------------------------------------------------------------------- /assets/img/slotitem/106.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/106.png -------------------------------------------------------------------------------- /assets/img/slotitem/107.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/107.png -------------------------------------------------------------------------------- /assets/img/slotitem/108.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/108.png -------------------------------------------------------------------------------- /assets/img/slotitem/109.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/109.png -------------------------------------------------------------------------------- /assets/img/slotitem/110.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/110.png -------------------------------------------------------------------------------- /assets/img/slotitem/111.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/111.png -------------------------------------------------------------------------------- /assets/img/slotitem/112.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/112.png -------------------------------------------------------------------------------- /assets/img/slotitem/113.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/113.png -------------------------------------------------------------------------------- /assets/img/slotitem/114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/114.png -------------------------------------------------------------------------------- /assets/img/slotitem/115.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/115.png -------------------------------------------------------------------------------- /assets/img/slotitem/116.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/116.png -------------------------------------------------------------------------------- /assets/img/slotitem/117.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/117.png -------------------------------------------------------------------------------- /assets/img/slotitem/118.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/118.png -------------------------------------------------------------------------------- /assets/img/slotitem/119.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/119.png -------------------------------------------------------------------------------- /assets/img/slotitem/120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/120.png -------------------------------------------------------------------------------- /assets/img/slotitem/121.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/121.png -------------------------------------------------------------------------------- /assets/img/slotitem/122.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/122.png -------------------------------------------------------------------------------- /assets/img/slotitem/123.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/123.png -------------------------------------------------------------------------------- /assets/img/slotitem/124.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/124.png -------------------------------------------------------------------------------- /assets/img/slotitem/125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/125.png -------------------------------------------------------------------------------- /assets/img/slotitem/126.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/126.png -------------------------------------------------------------------------------- /assets/img/slotitem/127.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/127.png -------------------------------------------------------------------------------- /assets/img/slotitem/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/128.png -------------------------------------------------------------------------------- /assets/img/slotitem/129.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/129.png -------------------------------------------------------------------------------- /assets/img/slotitem/130.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/130.png -------------------------------------------------------------------------------- /assets/img/slotitem/131.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/131.png -------------------------------------------------------------------------------- /assets/img/slotitem/132.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/132.png -------------------------------------------------------------------------------- /assets/img/slotitem/133.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/133.png -------------------------------------------------------------------------------- /assets/img/slotitem/134.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/134.png -------------------------------------------------------------------------------- /assets/img/slotitem/135.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/135.png -------------------------------------------------------------------------------- /assets/img/slotitem/136.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/136.png -------------------------------------------------------------------------------- /assets/img/slotitem/137.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/137.png -------------------------------------------------------------------------------- /assets/img/slotitem/138.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/138.png -------------------------------------------------------------------------------- /assets/img/slotitem/139.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/139.png -------------------------------------------------------------------------------- /assets/img/slotitem/140.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/140.png -------------------------------------------------------------------------------- /assets/img/slotitem/141.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/141.png -------------------------------------------------------------------------------- /assets/img/slotitem/142.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/142.png -------------------------------------------------------------------------------- /assets/img/slotitem/143.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/143.png -------------------------------------------------------------------------------- /assets/img/slotitem/144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/144.png -------------------------------------------------------------------------------- /assets/img/slotitem/145.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/145.png -------------------------------------------------------------------------------- /assets/img/slotitem/146.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/146.png -------------------------------------------------------------------------------- /assets/img/slotitem/147.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/147.png -------------------------------------------------------------------------------- /assets/img/slotitem/148.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/148.png -------------------------------------------------------------------------------- /assets/img/slotitem/149.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/149.png -------------------------------------------------------------------------------- /assets/img/slotitem/150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/150.png -------------------------------------------------------------------------------- /assets/img/slotitem/151.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/151.png -------------------------------------------------------------------------------- /assets/img/slotitem/152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/152.png -------------------------------------------------------------------------------- /assets/img/slotitem/154.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/154.png -------------------------------------------------------------------------------- /assets/img/slotitem/155.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/155.png -------------------------------------------------------------------------------- /assets/img/slotitem/156.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/156.png -------------------------------------------------------------------------------- /assets/img/slotitem/157.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/157.png -------------------------------------------------------------------------------- /assets/img/slotitem/158.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/slotitem/158.png -------------------------------------------------------------------------------- /assets/img/touchbar/lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/touchbar/lock.png -------------------------------------------------------------------------------- /assets/img/touchbar/media.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/touchbar/media.png -------------------------------------------------------------------------------- /assets/icons/poi_1024x1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/icons/poi_1024x1024.png -------------------------------------------------------------------------------- /assets/img/operation/build.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/operation/build.png -------------------------------------------------------------------------------- /assets/img/operation/repair.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/operation/repair.png -------------------------------------------------------------------------------- /assets/img/operation/sortie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/operation/sortie.png -------------------------------------------------------------------------------- /assets/img/touchbar/camera.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/touchbar/camera.png -------------------------------------------------------------------------------- /assets/img/touchbar/console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/touchbar/console.png -------------------------------------------------------------------------------- /assets/img/touchbar/refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/touchbar/refresh.png -------------------------------------------------------------------------------- /assets/img/touchbar/unlock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/touchbar/unlock.png -------------------------------------------------------------------------------- /index-plugin.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.stylelintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | # poi build directory 4 | build/download 5 | app_compiled/ 6 | dist/ 7 | -------------------------------------------------------------------------------- /assets/icons/poi_ribbon_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/icons/poi_ribbon_dark.png -------------------------------------------------------------------------------- /assets/icons/poi_ribbon_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/icons/poi_ribbon_light.png -------------------------------------------------------------------------------- /assets/img/touchbar/angle-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/touchbar/angle-left.png -------------------------------------------------------------------------------- /assets/img/touchbar/fullscreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/touchbar/fullscreen.png -------------------------------------------------------------------------------- /assets/img/touchbar/volume-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/touchbar/volume-off.png -------------------------------------------------------------------------------- /assets/img/touchbar/volume-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/touchbar/volume-up.png -------------------------------------------------------------------------------- /assets/icons/poi_ribbon_dark@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/icons/poi_ribbon_dark@2x.png -------------------------------------------------------------------------------- /assets/icons/poi_ribbon_dark@4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/icons/poi_ribbon_dark@4x.png -------------------------------------------------------------------------------- /assets/icons/poi_ribbon_light@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/icons/poi_ribbon_light@2x.png -------------------------------------------------------------------------------- /assets/icons/poi_ribbon_light@4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/icons/poi_ribbon_light@4x.png -------------------------------------------------------------------------------- /assets/img/operation/expedition.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/operation/expedition.png -------------------------------------------------------------------------------- /assets/img/touchbar/angle-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/touchbar/angle-right.png -------------------------------------------------------------------------------- /assets/img/touchbar/social-media.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/img/touchbar/social-media.png -------------------------------------------------------------------------------- /assets/icons/poi_ribbon_dark_256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/icons/poi_ribbon_dark_256x256.png -------------------------------------------------------------------------------- /assets/icons/poi_ribbon_light_256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/assets/icons/poi_ribbon_light_256x256.png -------------------------------------------------------------------------------- /views/components/main/assets/img/state/0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/views/components/main/assets/img/state/0.png -------------------------------------------------------------------------------- /views/components/main/assets/img/state/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/views/components/main/assets/img/state/1.png -------------------------------------------------------------------------------- /views/components/main/assets/img/state/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/views/components/main/assets/img/state/2.png -------------------------------------------------------------------------------- /views/components/main/assets/img/state/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/views/components/main/assets/img/state/3.png -------------------------------------------------------------------------------- /views/components/main/assets/img/state/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poooi/poi/HEAD/views/components/main/assets/img/state/4.png -------------------------------------------------------------------------------- /views/create-store.ts: -------------------------------------------------------------------------------- 1 | // this file is kept for compat 2 | 3 | // @ts-expect-error ts-migrate 4 | export * from './redux/create-store' 5 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | semi: false, 3 | singleQuote: true, 4 | trailingComma: 'all', 5 | printWidth: 100, 6 | } 7 | -------------------------------------------------------------------------------- /assets/js/disable-tab.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('keydown', (e) => { 2 | if (e.key === 'Tab') { 3 | e.preventDefault() 4 | } 5 | }) 6 | -------------------------------------------------------------------------------- /assets/data/fcd/meta.json: -------------------------------------------------------------------------------- 1 | [{"name":"map","version":"2025/11/30/01"},{"name":"shipavatar","version":"2018/09/08/02"},{"name":"shiptag","version":"2025/11/09/01"}] 2 | -------------------------------------------------------------------------------- /shims/vendor/header-case-normalizer.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'header-case-normalizer' { 2 | const normalize: (header: string) => string 3 | export default normalize 4 | } 5 | -------------------------------------------------------------------------------- /lib/constant.ts: -------------------------------------------------------------------------------- 1 | import path from 'path-extra' 2 | import CSON from 'cson' 3 | export default CSON.parseCSONFile(path.join(__dirname, '..', 'assets', 'data', 'constant.cson')) 4 | -------------------------------------------------------------------------------- /lib/__mocks__/fs-extra.es: -------------------------------------------------------------------------------- 1 | export const accessSync = jest.fn() 2 | export const writeFileSync = jest.fn() 3 | 4 | module.exports = { 5 | accessSync, 6 | writeFileSync, 7 | } 8 | -------------------------------------------------------------------------------- /babel-register.config.js: -------------------------------------------------------------------------------- 1 | const babelConfig = require('./babel.config') 2 | 3 | module.exports = { 4 | ...babelConfig, 5 | extensions: ['.es', '.ts', '.tsx'], 6 | cache: false, 7 | } 8 | -------------------------------------------------------------------------------- /views/redux/timers/index.es: -------------------------------------------------------------------------------- 1 | import { combineReducers } from '../combine-reducers' 2 | 3 | import cond from './cond' 4 | 5 | export const reducer = combineReducers({ 6 | cond, 7 | }) 8 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | try { 2 | require('./lib/cli') 3 | } catch (e) { 4 | require('@babel/register')(require('./babel-register.config')) 5 | require('./lib/cli') 6 | } finally { 7 | require('./app') 8 | } 9 | -------------------------------------------------------------------------------- /lib/__mocks__/cson.es: -------------------------------------------------------------------------------- 1 | export const parseCSONFile = jest.fn(() => ({})) 2 | 3 | export const stringify = jest.fn(() => 'CSON Stringified') 4 | 5 | module.exports = { 6 | parseCSONFile, 7 | stringify, 8 | } 9 | -------------------------------------------------------------------------------- /content/description/zh-CN.md: -------------------------------------------------------------------------------- 1 | --- 2 | namespace: setting 3 | language: zh-CN 4 | key: description_markdown 5 | --- 6 | 7 | poi 是一个开源的跨平台的「艦隊これくしょん」浏览器。poi 的行为与 Chrome 一致,原则上不提供任何影响收发包的功能。在基本功能之外,poi 还支持安装各类插件来提供扩展功能。 8 | -------------------------------------------------------------------------------- /content/description/zh-TW.md: -------------------------------------------------------------------------------- 1 | --- 2 | namespace: setting 3 | language: zh-TW 4 | key: description_markdown 5 | --- 6 | 7 | poi 是一個開源的跨平臺的「艦隊これくしょん」瀏覽器。poi 的行爲與 Chrome 一致,原則上不提供任何影響收發包的功能。在基本功能之外,poi 還支持安裝各類擴展程式來提供擴展功能。 8 | -------------------------------------------------------------------------------- /views/env-parts/dbg.ts: -------------------------------------------------------------------------------- 1 | import type { DbgInstance } from 'lib/debug' 2 | 3 | export const dbg: DbgInstance = window.isMain ? require('lib/debug') : undefined 4 | 5 | // @ts-expect-error backward compatibility 6 | window.dbg = dbg 7 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.es text eol=lf 2 | *.js text eol=lf 3 | *.json text eol=lf 4 | *.css text eol=lf 5 | *.md text eol=lf 6 | *.yml text eol=lf 7 | *.svg text eol=lf 8 | *.ts text eol=lf 9 | *.tsx text eol=lf 10 | *.svg text eol=lf 11 | -------------------------------------------------------------------------------- /.mention-bot: -------------------------------------------------------------------------------- 1 | { 2 | "maxReviewers": 5, 3 | "numFilesToCheck": 10, 4 | "findPotentialReviewers": true, 5 | "fileBlacklist": ["*.md"], 6 | "userBlacklist": [], 7 | "userBlacklistForPR": [], 8 | "requiredOrgs": [], 9 | "actions": ["opened"] 10 | } 11 | -------------------------------------------------------------------------------- /views/redux/wctf.es: -------------------------------------------------------------------------------- 1 | export const reducer = (state = {}, { type, payload }) => { 2 | switch (type) { 3 | case '@@wctf-db-update': { 4 | return { 5 | ...state, 6 | ...payload, 7 | } 8 | } 9 | } 10 | return state 11 | } 12 | -------------------------------------------------------------------------------- /content/description/ja-JP.md: -------------------------------------------------------------------------------- 1 | --- 2 | namespace: setting 3 | language: ja-JP 4 | key: description_markdown 5 | --- 6 | 7 | poi はマルチプラットフォ ー ム対応オ ー プンソ ー ス「艦隊これくしょん」専用ブラウザです。ゲ ー ムにおける受送信仕様について、poi は Chrome と異なりがなく、原則的にはパケット通信に影響を与える機能は一切提供しません。poi 本体は基本的なブラウジング機能を提供し、拡張機能はプラグインで実現する仕様となります。 8 | -------------------------------------------------------------------------------- /views/redux/info/__tests__/__fixtures__/api_req_air_corps_change_name.json: -------------------------------------------------------------------------------- 1 | {"method":"POST","path":"/kcsapi/api_req_air_corps/change_name","body":{"api_result":1,"api_result_msg":"成功"},"postBody":{"api_verno":"1","api_area_id":"6","api_base_id":"1","api_name":"第1基地航空隊"},"time":1639774787802} 2 | -------------------------------------------------------------------------------- /views/redux/info/__tests__/__fixtures__/api_req_air_corps_set_action.json: -------------------------------------------------------------------------------- 1 | {"method":"POST","path":"/kcsapi/api_req_air_corps/set_action","body":{"api_result":1,"api_result_msg":"成功"},"postBody":{"api_verno":"1","api_area_id":"6","api_base_id":"1,2,3","api_action_kind":"0,0,0"},"time":1576947577250} 2 | -------------------------------------------------------------------------------- /views/redux/misc.es: -------------------------------------------------------------------------------- 1 | import { combineReducers } from './combine-reducers' 2 | 3 | function canNotify(state = false, { type }) { 4 | if (type === '@@Response/kcsapi/api_port/port') return true 5 | return state 6 | } 7 | 8 | export default combineReducers({ 9 | canNotify, 10 | }) 11 | -------------------------------------------------------------------------------- /assets/js/webview-preload.js: -------------------------------------------------------------------------------- 1 | const remote = require('@electron/remote') 2 | window.ipc = remote.require('./lib/ipc') 3 | 4 | require('./xhr-hack') 5 | require('./resource-hack') 6 | require('./page-align') 7 | require('./cookie-hack') 8 | require('./disable-tab') 9 | require('./capture-page') 10 | -------------------------------------------------------------------------------- /views/utils/color.ts: -------------------------------------------------------------------------------- 1 | export const shipAvatarColor = { 2 | GREY_BLUE: '#90caf960', 3 | GREEN: '#8bc34a60', 4 | YELLOW: '#fdd83560', 5 | ORANGE: '#fb8c0060', 6 | RED: '#ef535060', 7 | BLUE: '#1e88e560', 8 | PURPLE: '#8e24aa80', 9 | WHITE: '#e0e0e060', 10 | BLACK: '#00000060', 11 | } 12 | -------------------------------------------------------------------------------- /views/components/etc/window-env.ts: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react' 2 | 3 | interface WindowEnvContext { 4 | window?: Window 5 | mountPoint?: HTMLElement 6 | } 7 | 8 | export const WindowEnv = createContext({ 9 | window: undefined, 10 | mountPoint: undefined, 11 | }) 12 | -------------------------------------------------------------------------------- /views/redux/info/server.es: -------------------------------------------------------------------------------- 1 | const initState = { 2 | ip: null, 3 | id: null, 4 | name: null, 5 | } 6 | 7 | export const reducer = (state = initState, action) => { 8 | if (action.type === '@@ServerReady') { 9 | const { serverInfo } = action 10 | return serverInfo 11 | } 12 | 13 | return state 14 | } 15 | -------------------------------------------------------------------------------- /views/env-parts/config.ts: -------------------------------------------------------------------------------- 1 | import * as remote from '@electron/remote' 2 | import type { ConfigInstance } from 'lib/config' 3 | export type { ConfigInstance, Config } from 'lib/config' 4 | 5 | export const config: ConfigInstance = remote.require('./lib/config') 6 | 7 | // @ts-expect-error backward compatibility 8 | window.config = config 9 | -------------------------------------------------------------------------------- /content/description/en-US.md: -------------------------------------------------------------------------------- 1 | --- 2 | namespace: setting 3 | language: en-US 4 | key: description_markdown 5 | --- 6 | 7 | poi is an open source Kancolle browser. poi behaves the same as Chrome and won't modify game data, packets or implement bots/macros. The main poi provides basic functionalities and is complemented by a variety of plugins. 8 | -------------------------------------------------------------------------------- /views/components/settings/gaming/index.es: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import { NotificationConfig } from './notification-config' 4 | import { PreSortieConfig } from './pre-sortie-config' 5 | 6 | export const GamingConfig = () => ( 7 |
8 | 9 | 10 |
11 | ) 12 | -------------------------------------------------------------------------------- /shims/vendor/socks5-client.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'socks5-client' { 2 | import net from 'net' 3 | interface ConnectionOptions { 4 | socksHost: string 5 | socksPort: string | number 6 | host: string 7 | port: string | number 8 | } 9 | export const createConnection: (options: ConnectionOptions) => net.Socket 10 | } 11 | -------------------------------------------------------------------------------- /content/welcome/ja-JP.md: -------------------------------------------------------------------------------- 1 | --- 2 | namespace: others 3 | language: ja-JP 4 | key: welcome_markdown 5 | --- 6 | poi {{version}} へようこそっぽい!使用の前に、あなたのためのいくつかのヒントがあります 7 | 8 | __poi はあなたのゲームデータを決して変更しませんが、信頼できるリリースとプラグインを使用してください__ 9 | 10 | パフォーマンスが低下している場合は、一部のプラグインを無効にすることが効きます 11 | 12 | ----- 13 | 14 | 提案がありますか? 開発に参加したいですか? [ここに注目](https://github.com/poooi/poi) 15 | -------------------------------------------------------------------------------- /lint-staged.config.js: -------------------------------------------------------------------------------- 1 | const lintScripts = ['eslint --fix'] 2 | const lintViews = [...lintScripts, 'stylelint --config .stylelintrc.js'] 3 | 4 | module.exports = { 5 | '*.js': lintScripts, 6 | '*.ts': lintScripts, 7 | '*.es': lintViews, 8 | '*.tsx': lintViews, 9 | '*.css': ['stylelint --config .stylelintrc.js'], 10 | '*.md': ['prettier --write'], 11 | } 12 | -------------------------------------------------------------------------------- /views/services/worker.es: -------------------------------------------------------------------------------- 1 | import { writeFile } from 'fs-extra' 2 | 3 | /* 4 | * ioWorker is deprecated. this is a dummy worker for backward compatibility 5 | */ 6 | 7 | export const ioWorker = { 8 | initialize: () => null, 9 | port: { 10 | postMessage: ([type, path, data]) => { 11 | writeFile(path, JSON.stringify(data)) 12 | }, 13 | }, 14 | } 15 | -------------------------------------------------------------------------------- /content/certerror/zh-CN.md: -------------------------------------------------------------------------------- 1 | --- 2 | namespace: others 3 | language: zh-CN 4 | key: cert_error_markdown 5 | --- 6 | 7 | 发生了一个证书错误。这可能是因为你正在使用一个带有自签证书的代理,或者你正在遭到中间人攻击。 8 | 9 | 这个证书被 **{{name}}** 签发,它的 sha256 特征值为 **{{value}}**. 10 | 11 | 选择 `信任` 将会允许所有使用了这个证书的链接,否则它们将继续被拦截 12 | 13 | 请在这些证书的发布者是受信任的(比如,你的代理的提供商)情况下选择信任他们 14 | 15 | 如果你在使用带有自签证书的代理,推荐直接导入代理提供的根证书而非在这里允许链接。详情请咨询代理客服。 16 | -------------------------------------------------------------------------------- /content/certerror/zh-TW.md: -------------------------------------------------------------------------------- 1 | --- 2 | namespace: others 3 | language: zh-TW 4 | key: cert_error_markdown 5 | --- 6 | 7 | 發生了一個證書錯誤。這可能是因為你正在使用一個帶有自簽證書的代理,或者你正在遭到中間人攻擊。 8 | 9 | 這個證書被 **{{name}}** 簽發,它的 sha256 特徵值為 **{{value}}**. 10 | 11 | 選擇 `信任` 將會允許所有使用了這個證書的連結,否則它們將繼續被攔截 12 | 13 | 請在這些證書的釋出者是受信任的(比如,你的代理的提供商)情況下選擇信任他們 14 | 15 | 如果你在使用帶有自簽證書的代理,推薦直接匯入代理提供的根證書而非在這裡允許連結。詳情請諮詢代理客服。 16 | -------------------------------------------------------------------------------- /setup-tests.ts: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import 'snapshot-diff' 3 | import 'snapshot-diff/extend-expect' 4 | 5 | // @ts-expect-error chalk stops color output under CI mode, we manually enable it 6 | process.env.FORCE_COLOR = 1 7 | 8 | // to make path related results consistent across different OS 9 | global.ROOT = __dirname 10 | global.EXROOT = path.join(__dirname, 'exroot') 11 | -------------------------------------------------------------------------------- /views/redux/ui.es: -------------------------------------------------------------------------------- 1 | const initState = { 2 | activeMainTab: 'main-view', 3 | activeFleetId: 0, 4 | } 5 | 6 | export function reducer(state = initState, { type, tabInfo, themes }) { 7 | switch (type) { 8 | case '@@TabSwitch': { 9 | return { 10 | ...state, 11 | ...tabInfo, 12 | } 13 | } 14 | default: 15 | return state 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/devtool.ts: -------------------------------------------------------------------------------- 1 | import installExtension, { 2 | REACT_DEVELOPER_TOOLS, 3 | REDUX_DEVTOOLS, 4 | } from 'electron-devtools-installer' 5 | 6 | import { log, error } from './utils' 7 | 8 | installExtension([REACT_DEVELOPER_TOOLS, REDUX_DEVTOOLS], { 9 | loadExtensionOptions: { allowFileAccess: true }, 10 | }) 11 | .then((name) => log(`${name} is added`)) 12 | .catch((err) => error('An error occurred: ', err)) 13 | -------------------------------------------------------------------------------- /views/polyfills/react-fontawesome.es: -------------------------------------------------------------------------------- 1 | /** 2 | * polyfill for react-fontawesome 3 | */ 4 | import { library } from '@fortawesome/fontawesome-svg-core' 5 | import { fas } from '@fortawesome/free-solid-svg-icons' 6 | import { far } from '@fortawesome/free-regular-svg-icons' 7 | import { fab } from '@fortawesome/free-brands-svg-icons' 8 | 9 | import '@skagami/react-fontawesome/inject' 10 | library.add(fas, far, fab) 11 | -------------------------------------------------------------------------------- /views/services/resource-notifier.es: -------------------------------------------------------------------------------- 1 | import * as remote from '@electron/remote' 2 | import EventEmitter from 'events' 3 | 4 | const { session } = remote 5 | 6 | export const ResourceNotifier = new (class ResourceNotifier extends EventEmitter { 7 | constructor() { 8 | super() 9 | session.defaultSession.webRequest.onSendHeaders((detail) => { 10 | this.emit('request', detail) 11 | }) 12 | } 13 | })() 14 | -------------------------------------------------------------------------------- /views/components/etc/styled-components.ts: -------------------------------------------------------------------------------- 1 | import { styled } from 'styled-components' 2 | 3 | export const InfoTooltipEntry = styled.div` 4 | display: flex; 5 | min-width: 100px; 6 | text-align: left; 7 | ` 8 | 9 | export const InfoTooltipItem = styled.span` 10 | flex-grow: 1; 11 | margin-right: 2em; 12 | ` 13 | 14 | export const InfoTooltip = styled.div` 15 | max-width: initial; 16 | width: fit-content; 17 | ` 18 | -------------------------------------------------------------------------------- /views/redux/info/__tests__/__fixtures__/api_req_air_corps_supply.json: -------------------------------------------------------------------------------- 1 | {"method":"POST","path":"/kcsapi/api_req_air_corps/supply","body":{"api_after_fuel":102760,"api_after_bauxite":71894,"api_distance":{"api_base":5,"api_bonus":0},"api_plane_info":[{"api_squadron_id":3,"api_state":1,"api_slotid":52792,"api_count":18,"api_max_count":18,"api_cond":1}]},"postBody":{"api_verno":"1","api_area_id":"6","api_base_id":"1","api_squadron_id":"3"},"time":1612280427339} 2 | -------------------------------------------------------------------------------- /views/theme.ts: -------------------------------------------------------------------------------- 1 | import { Colors } from '@blueprintjs/core' 2 | import { LegacyColors } from '@blueprintjs/colors' 3 | 4 | export const darkTheme = { 5 | ...LegacyColors, 6 | ...Colors, 7 | name: 'bp5-dark', 8 | variant: 'dark', 9 | slotBg: 'rgb(33 33 33 / 0.7)', 10 | } 11 | 12 | export const lightTheme = { 13 | ...LegacyColors, 14 | ...Colors, 15 | name: 'bp5-light', 16 | variant: 'light', 17 | slotBg: 'rgb(255 255 255 / 0.7)', 18 | } 19 | -------------------------------------------------------------------------------- /lib/native-theme-helper.ts: -------------------------------------------------------------------------------- 1 | import config from './config' 2 | import { nativeTheme } from 'electron' 3 | 4 | const getThemeSource = (value: string) => (value === 'dark' ? 'dark' : 'light') 5 | 6 | config.on('config.set', (key: string, value: string) => { 7 | if (key === 'poi.appearance.theme') { 8 | nativeTheme.themeSource = getThemeSource(value) 9 | } 10 | }) 11 | 12 | nativeTheme.themeSource = getThemeSource(config.get('poi.appearance.theme', 'dark')) 13 | -------------------------------------------------------------------------------- /views/components/etc/custom-tag.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactNode, FC } from 'react' 2 | 3 | interface CustomTagProps { 4 | tag?: keyof JSX.IntrinsicElements 5 | children?: ReactNode 6 | className?: string 7 | } 8 | 9 | export const CustomTag: FC = ({ tag = 'div', className, children, ...props }: CustomTagProps) => { 10 | // @ts-expect-error wrong type definition 11 | props.class = className 12 | return React.createElement(tag, props, children) 13 | } 14 | -------------------------------------------------------------------------------- /assets/data/mirror.json: -------------------------------------------------------------------------------- 1 | { 2 | "taobao": { 3 | "name": "Taobao (CN)", 4 | "menuname": "registry.npmmirror.com", 5 | "server": "https://registry.npmmirror.com/" 6 | }, 7 | "cnpm": { 8 | "name": "cnpm (CN)", 9 | "menuname": "r.cnpmjs.org", 10 | "server": "https://r.cnpmjs.org/" 11 | }, 12 | "npm": { 13 | "name": "npmjs (US)", 14 | "menuname": "registry.npmjs.org", 15 | "server": "https://registry.npmjs.org/" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /content/certerror/ja-JP.md: -------------------------------------------------------------------------------- 1 | --- 2 | namespace: others 3 | language: ja-JP 4 | key: cert_error_markdown 5 | --- 6 | 7 | 証明書エラーが発生しました。 自己割り当ての証明書を持つプロキシを使用しているか、中間者攻撃を受けている可能性があります。 8 | 9 | この証明書は **{{name}}** によって割り当てられ、その sha256 値は **{{value}}** です。 10 | 11 | `信頼` を押すと、この証明書を使用するすべての接続が許可されます。そうしないと、これらの接続はブロックされたままになります。 12 | 13 | この証明書は、信頼されたプロバイダ(プロキシプロバイダなど)によって割り当てられている場合にのみ信頼してください。 14 | 15 | 自己割り当て証明書付きのプロキシを使用している場合は、プロキシプロバイダの自己割り当てルート CA をインポートすることをお勧めします。 16 | -------------------------------------------------------------------------------- /lib/screenshot.ts: -------------------------------------------------------------------------------- 1 | import { ipcMain, Rectangle, ResizeOptions, webContents } from 'electron' 2 | 3 | ipcMain.handle( 4 | 'screenshot::get', 5 | async (event, id: number, rect: Rectangle, actualSize?: ResizeOptions) => { 6 | const webContent = webContents.fromId(id) 7 | if (webContent) { 8 | const image = await webContent.capturePage(rect) 9 | return (actualSize ? image.resize(actualSize) : image).toDataURL() 10 | } 11 | return undefined 12 | }, 13 | ) 14 | -------------------------------------------------------------------------------- /views/env-parts/modal.ts: -------------------------------------------------------------------------------- 1 | import { ReactNode } from 'react' 2 | import { ButtonData, modalEventEmitter } from 'views/components/etc/modal' 3 | 4 | export const toggleModal = ( 5 | title: string, 6 | content: ReactNode, 7 | footer: ButtonData[], 8 | onClosing?: () => void, 9 | ) => { 10 | modalEventEmitter.emit({ 11 | title, 12 | content, 13 | footer, 14 | onClosing, 15 | }) 16 | } 17 | 18 | // @ts-expect-error backward compatibility 19 | window.toggleModal = toggleModal 20 | -------------------------------------------------------------------------------- /assets/svg/COPYRIGHT.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2 | 2015, 2016 [edwardaaaa](https://github.com/edwardaaaa) 3 | 2016, 2017 [かがみ](https://github.com/KagamiChan) 4 | 2017 [KochiyaOcean](https://github.com/KochiyaOcean) 5 | 2017 [Artoria](https://github.com/Artoria-0x04) 6 | All rights reserved. 7 | 8 | THESE SVG ICONS ARE **NOT** GOVERNED BY THE MIT LICENSE. 9 | 10 | ICONS HERE CAN **ONLY** BE USED IN POI, INCLUDING PLUGINS, EXTENSIONS AND DERIVATIVES OF POI. ANY OTHER USAGE WITHOUT EXPLICIT PERMISSION IS PROHIBITED. 11 | -------------------------------------------------------------------------------- /assets/svg/slotitem/0.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 0 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /views/redux/info/__tests__/__fixtures__/api_req_air_corps_set_plane.json: -------------------------------------------------------------------------------- 1 | {"method":"POST","path":"/kcsapi/api_req_air_corps/set_plane","body":{"api_distance":{"api_base":5,"api_bonus":0},"api_plane_info":[{"api_squadron_id":1,"api_state":1,"api_slotid":52792,"api_count":15,"api_max_count":18,"api_cond":1},{"api_squadron_id":3,"api_state":1,"api_slotid":26059,"api_count":18,"api_max_count":18,"api_cond":1}]},"postBody":{"api_verno":"1","api_area_id":"6","api_base_id":"1","api_squadron_id":"1","api_item_id":"52792"},"time":1639782334264} 2 | -------------------------------------------------------------------------------- /content/welcome/zh-TW.md: -------------------------------------------------------------------------------- 1 | --- 2 | namespace: others 3 | language: zh-TW 4 | key: welcome_markdown 5 | --- 6 | 7 | 誒嘿!歡迎使用 poi {{version}}!使用之前看看下面! 8 | 9 | **poi 不會修改任何遊戲內的發包與收包,但是請使用可信的 poi 版本和可信的擴展程式!** 10 | 11 | poi 默認不使用代理。更改代理的設置在設置面板中可以找到。 12 | 13 | - 烤餅乾的用戶請打開設定 DMM 地區 Cookie; 14 | - 使用自己本地的 Shadowsocks 或者 Socks5 代理的選擇 Socks5 代理; 15 | - 使用 VPN 的選擇不使用代理就好了。 16 | 17 | 如果 poi 的運行不流暢,可以在設置中關閉或卸載一部分擴展程式。 18 | 19 | 更多幫助與說明請[移步 Wiki](https://github.com/poooi/poi/wiki)。 20 | 21 | --- 22 | 23 | 喜歡 poi?對 poi 有建議?想要貢獻代碼或者插件?[請點擊這裏](https://github.com/poooi/poi) 24 | -------------------------------------------------------------------------------- /views/polyfills/react-i18next.es: -------------------------------------------------------------------------------- 1 | /** 2 | * FIXME: remove this polyfill when plugins begin to use react-i18next@>10 3 | */ 4 | /* eslint-disable import/namespace */ 5 | import * as ReactI18next from 'react-i18next' 6 | 7 | /* eslint-disable no-import-assign */ 8 | ReactI18next.translate = ReactI18next.withNamespaces = ReactI18next.withTranslation 9 | ReactI18next.reactI18nextModule = ReactI18next.initReactI18next 10 | ReactI18next.NamespacesConsumer = ReactI18next.Translation 11 | ReactI18next.Interpolate = ReactI18next.Trans 12 | /* eslint-enable no-import-assign */ 13 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.tabSize": 2, 3 | "files.insertFinalNewline": true, 4 | "files.associations": { 5 | "*.es": "javascriptreact" 6 | }, 7 | "files.trimTrailingWhitespace": true, 8 | "javascript.validate.enable": false, 9 | "editor.detectIndentation": false, 10 | "eslint.validate": [ 11 | "javascript", 12 | "javascriptreact", 13 | "typescript", 14 | "typescriptreact" 15 | ], 16 | "typescript.tsdk": "node_modules/typescript/lib", 17 | "editor.codeActionsOnSave": { 18 | "source.fixAll.eslint": "explicit" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /i18n/data/ko-KR.json: -------------------------------------------------------------------------------- 1 | { 2 | "HP": "내구", 3 | "Armor": "장갑", 4 | "Firepower": "화력", 5 | "Torpedo": "뇌장", 6 | "Speed": "속력", 7 | "Bombing": "폭장", 8 | "AA": "대공", 9 | "ASW": "대잠", 10 | "Accuracy": "명중", 11 | "Torpedo Accuracy": "뇌격명중", 12 | "Evasion": "회피", 13 | "Torpedo Evasion": "뇌격회피", 14 | "Bombing Evasion": "폭격회피", 15 | "LOS": "색적", 16 | "Anti-LOS": "색적방해", 17 | "Luck": "운", 18 | "Range": "사거리", 19 | "Short": "단", 20 | "Medium": "중", 21 | "Long": "장", 22 | "Very Long": "초장", 23 | "Interception": "영격", 24 | "Anti-Bomber": "대폭" 25 | } 26 | -------------------------------------------------------------------------------- /i18n/data/ja-JP.json: -------------------------------------------------------------------------------- 1 | { 2 | "HP": "耐久", 3 | "Armor": "装甲", 4 | "Firepower": "火力", 5 | "Torpedo": "雷装", 6 | "Speed": "速力", 7 | "Bombing": "爆装", 8 | "AA": "対空", 9 | "ASW": "対潜", 10 | "Accuracy": "命中", 11 | "Torpedo Accuracy": "雷撃命中", 12 | "Evasion": "回避", 13 | "Torpedo Evasion": "雷撃回避", 14 | "Bombing Evasion": "爆撃回避", 15 | "LOS": "索敵", 16 | "Anti-LOS": "索敵妨害", 17 | "Luck": "運", 18 | "Range": "射程", 19 | "Short": "短", 20 | "Medium": "中", 21 | "Long": "長", 22 | "Very Long": "超長", 23 | "Interception": "迎擊", 24 | "Anti-Bomber": "対爆", 25 | "Lv": "レベル", 26 | "FP": "火力" 27 | } 28 | -------------------------------------------------------------------------------- /content/welcome/zh-CN.md: -------------------------------------------------------------------------------- 1 | --- 2 | namespace: others 3 | language: zh-CN 4 | key: welcome_markdown 5 | --- 6 | 7 | 诶嘿!欢迎使用 poi {{version}}!使用之前看看下面! 8 | 9 | **poi 不会修改任何游戏内的发包与收包,但是请使用可信的 poi 版本和可信的插件!** 10 | 11 | poi 默认不使用代理。更改代理的设置在设置面板中可以找到。 12 | 13 | - 使用岛风 Go 的选择 HTTP 代理,默认地址是 127.0.0.1,端口 8099; 14 | - 使用自己本地的 Shadowsocks 或者 Socks5 代理的选择 Socks5 代理; 15 | - 使用 VPN 的选择不使用代理就好了。 16 | 17 | 如果游戏界面没有完全对齐,可以手动调整一下内容大小,布局会自动适配。 18 | 19 | 如果 poi 的运行不流畅,可以在设置中关闭或卸载一部分插件。 20 | 21 | 更多帮助与说明请[移步 Wiki](https://github.com/poooi/poi/wiki)。 22 | 23 | --- 24 | 25 | 喜欢 poi?对 poi 有建议?想要贡献代码或者插件?[请点击这里](https://github.com/poooi/poi) 26 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "commonjs", 5 | "lib": [ 6 | "ESNext", 7 | "DOM" 8 | ], 9 | "jsx": "react", 10 | "strict": true, 11 | "baseUrl": "./", 12 | "paths": { 13 | "views/*": [ 14 | "./views/*" 15 | ] 16 | }, 17 | "typeRoots": [ 18 | "./node_modules/@types", 19 | "./shims/vendor" 20 | ], 21 | "esModuleInterop": true, 22 | "forceConsistentCasingInFileNames": true, 23 | "resolveJsonModule": true, 24 | "importHelpers": true, 25 | "moduleResolution": "node", 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /shims/global.d.ts: -------------------------------------------------------------------------------- 1 | interface ToastConfig { 2 | type: string 3 | title: string 4 | } 5 | 6 | declare global { 7 | namespace NodeJS { 8 | interface Global { 9 | EXROOT: string 10 | ROOT: string 11 | DEFAULT_CACHE_PATH: string 12 | } 13 | } 14 | interface Window { 15 | toast: (message: string, config: ToastConfig) => void 16 | } 17 | // let and const do not show up on globalThis 18 | /* eslint-disable no-var */ 19 | var EXROOT: string 20 | var ROOT: string 21 | var DEFAULT_CACHE_PATH: string 22 | var isMain: boolean | undefined 23 | /* eslint-enable no-var */ 24 | } 25 | 26 | export {} 27 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 功能建议 3 | about: Suggest an idea 帮助我们改进 poi 功能 4 | --- 5 | 6 | **poi 版本 / poi version:** 7 | 8 | **操作系统 / OS:** 9 | 10 | 14 | 15 | **插件名和版本 / Plugin name & version:** 16 | 17 | 21 | 22 | **功能的详细描述 / Details on the feature:** 23 | 24 | 26 | -------------------------------------------------------------------------------- /content/certerror/en-US.md: -------------------------------------------------------------------------------- 1 | --- 2 | namespace: others 3 | language: en-US 4 | key: cert_error_markdown 5 | --- 6 | 7 | There is a certificate error occurred. It may be caused by using a proxy with self-signed certificate, or under a man-in-the-middle attack. 8 | 9 | This certificate is assigned by **{{name}}** and its sha256 value is **{{value}}**. 10 | 11 | Press `Trust` will allow all connections using this certificate, or these connections will remain blocked. 12 | 13 | Trust this certificate only when its assgined by a trusted provider(e.g. your proxy provider). 14 | 15 | If you're using a proxy with self-signed certificate, it's recommended to import your proxy providers self-signed root CA. 16 | -------------------------------------------------------------------------------- /i18n/menu/en-US.json: -------------------------------------------------------------------------------- 1 | { 2 | "Preferences": "Preferences...", 3 | "Hide poi": "Hide poi", 4 | "Show poi": "Show poi", 5 | "Resizable": "Resizable", 6 | "Reset Views": "Reset views", 7 | "Always on top": "Always on top", 8 | "Quit poi": "Quit poi", 9 | "View": "View", 10 | "Reload": "Reload", 11 | "Stop": "Stop", 12 | "Developer Tools": "Developer Tools", 13 | "Developer Tools of WebView": "Developer Tools of WebView", 14 | "Themes": "Themes", 15 | "Next Theme": "Next Theme", 16 | "Previous Theme": "Previous Theme", 17 | "Help": "Help", 18 | "Wiki": "Wiki", 19 | "poi Statistics": "poi Statistics", 20 | "Report Issue": "Report Issue", 21 | "Search Issues": "Search Issues" 22 | } 23 | -------------------------------------------------------------------------------- /views/redux/config/index.ts: -------------------------------------------------------------------------------- 1 | import { cloneDeep } from 'lodash' 2 | import { type Config, config } from 'views/env-parts/config' 3 | 4 | import { reduxSet } from 'views/utils/tools' 5 | import { createConfigAction } from '../actions' 6 | import { createSlice } from '@reduxjs/toolkit' 7 | 8 | const configSlice = createSlice({ 9 | name: 'config', 10 | initialState: cloneDeep(config.get('')) as Config, 11 | reducers: {}, 12 | extraReducers: (builder) => { 13 | builder.addCase(createConfigAction, (state, { payload }) => { 14 | const { path, value } = payload 15 | return reduxSet(state, path.split('.'), value) 16 | }) 17 | }, 18 | }) 19 | 20 | export const reducer = configSlice.reducer 21 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | const _ = require('lodash') 4 | 5 | /** @type { jest.ProjectConfig } */ 6 | module.exports = { 7 | clearMocks: true, 8 | coverageDirectory: 'coverage', 9 | transform: { 10 | '^.+\\.(es|ts|tsx)$': 'babel-jest', 11 | }, 12 | moduleFileExtensions: ['js', 'es', 'json', 'jsx', 'ts', 'tsx', 'node'], 13 | moduleNameMapper: { 14 | '^views/(.*)': '/views/$1', 15 | }, 16 | testMatch: ['**/__tests__/**/*.[ejt]s?(x)', '**/?(*.)+(spec|test).[ejt]s?(x)'], 17 | setupFilesAfterEnv: ['./setup-tests.ts'], 18 | collectCoverageFrom: ['lib', 'views', 'build'].map((dir) => `./${dir}/**/*.[ejt]s?(x)`), 19 | collectCoverage: _.toLower(process.env.CI) === 'true', 20 | } 21 | -------------------------------------------------------------------------------- /lib/updater.ts: -------------------------------------------------------------------------------- 1 | import { autoUpdater } from 'electron-updater' 2 | import config from './config' 3 | 4 | import Logger from 'electron-log' 5 | 6 | autoUpdater.logger = Logger 7 | ;(autoUpdater.logger as typeof Logger).transports.file.level = 'info' 8 | 9 | autoUpdater.setFeedURL({ 10 | provider: 'generic', 11 | url: 'https://update.poi.moe/dist', 12 | channel: config.get('poi.update.beta', false) ? 'beta' : 'latest', 13 | }) 14 | autoUpdater.autoDownload = false 15 | 16 | export function changeChannel(channel: string) { 17 | autoUpdater.setFeedURL({ 18 | provider: 'generic', 19 | url: 'https://update.poi.moe/dist', 20 | channel, 21 | }) 22 | autoUpdater.autoDownload = false 23 | } 24 | 25 | export const updater = autoUpdater 26 | -------------------------------------------------------------------------------- /fcd/README.md: -------------------------------------------------------------------------------- 1 | # Frequently Changed Data (FCD) 2 | 3 | FCD 为频繁更新的游戏数据。这些数据不能从游戏 API 中简单获得,需要开发者手动更新维护。 4 | 5 | 6 | ## FCD 判断标准 7 | 8 | 满足以下条件的数据可以考虑放到 FCD: 9 | 1. 是游戏数据,但只能由开发者手动更新 10 | 2. 有两个或以上插件(含本体)使用的数据 11 | 12 | 以下数据不适合放到 FCD: 13 | 1. 该数据即该插件的核心功能,如装备改修插件。 14 | 15 | 16 | ## 数据文件 17 | 一个文件必须包含两个部分:meta 和 data。 18 | 19 | ### meta 20 | 必须包含: 21 | * `name`:数据的标识。 22 | * `version`:数据的版本,格式为`yyyy/MM/dd/vv`。 23 | 24 | 可选包含: 25 | * `filename`:数据文件名,默认为`${name}.json`。 26 | 27 | ### data 28 | data 数据将会直接插入`store.fcd.${name}`路径。 29 | 30 | ### 范例 31 | ``` 32 | { 33 | "meta": { 34 | "name": "example", 35 | "version": "1984/02/30/01" 36 | }, 37 | "data": [ 38 | 1, 1, 2, 3, 5, 8 39 | ] 40 | } 41 | ``` 42 | ``` 43 | store.fcd.example = [1, 1, 2, 3, 5, 8] 44 | ``` -------------------------------------------------------------------------------- /views/components/etc/resize-sensor.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useMemo, useState } from 'react' 2 | 3 | interface ResizeSensorProps { 4 | onResize: ResizeObserverCallback 5 | children: React.ReactElement 6 | } 7 | 8 | export const ResizeSensor: React.FC = ({ onResize, children }) => { 9 | const [ref, setRef] = useState(null) 10 | 11 | const observer = useMemo(() => new ResizeObserver(onResize), [onResize]) 12 | 13 | useEffect(() => { 14 | if (ref) { 15 | observer.observe(ref) 16 | } 17 | return () => { 18 | if (ref) { 19 | observer.unobserve(ref) 20 | } 21 | } 22 | }, [ref, observer]) 23 | 24 | return React.cloneElement(React.Children.only(children), { ref: setRef }) 25 | } 26 | -------------------------------------------------------------------------------- /views/redux/ipc.es: -------------------------------------------------------------------------------- 1 | import { mapValues, omit } from 'lodash' 2 | 3 | const INIT_STATE = {} 4 | 5 | export const reducer = ( 6 | state = INIT_STATE, 7 | { type, value: { scope, opts, keys } = {}, content }, 8 | ) => { 9 | switch (type) { 10 | case '@@initIPC': { 11 | return { 12 | ...state, 13 | ...content, 14 | } 15 | } 16 | case '@@registerIPC': { 17 | return { 18 | ...state, 19 | [scope]: mapValues(opts, () => true), 20 | } 21 | } 22 | case '@@unregisterIPC': { 23 | return { 24 | ...state, 25 | [scope]: omit(state[scope], keys), 26 | } 27 | } 28 | case '@@unregisterAllIPC': { 29 | return omit(state, scope) 30 | } 31 | } 32 | return state 33 | } 34 | -------------------------------------------------------------------------------- /views/redux/fcd.es: -------------------------------------------------------------------------------- 1 | const initState = { 2 | version: {}, 3 | } 4 | 5 | export function reducer(state = initState, { type, value }) { 6 | switch (type) { 7 | case '@@updateFCD': 8 | if (value.data && value.meta) { 9 | const { name, version } = value.meta 10 | if (name && version) { 11 | state = { 12 | ...state, 13 | version: { 14 | ...state.version, 15 | [name]: version, 16 | }, 17 | [name]: value.data, 18 | } 19 | } 20 | } 21 | break 22 | case '@@replaceFCD': 23 | if (value.path && value.data) { 24 | state = { 25 | ...state, 26 | [value.path]: value.data, 27 | } 28 | } 29 | } 30 | return state 31 | } 32 | -------------------------------------------------------------------------------- /views/services/google-analytics.es: -------------------------------------------------------------------------------- 1 | /* global ga, config, getStore */ 2 | 3 | import { observer, observe } from 'redux-observers' 4 | import { store } from 'views/create-store' 5 | 6 | let heartbeat = null 7 | 8 | const handleMemberIdChange = (dispatch, current, previous) => { 9 | ga('set', 'userId', current) 10 | ga('send', 'pageview') 11 | if (!heartbeat) { 12 | heartbeat = setInterval(() => { 13 | ga('send', 'event', 'heartbeat') 14 | }, 240000) 15 | } 16 | } 17 | 18 | const memberIdObserver = observer((state) => state.info.basic.api_member_id, handleMemberIdChange) 19 | 20 | if (config.get('poi.misc.analytics', true)) { 21 | if (getStore('info.basic.api_member_id')) { 22 | handleMemberIdChange(null, window.getStore('info.basic.api_member_id')) 23 | } 24 | observe(store, [memberIdObserver]) 25 | } 26 | -------------------------------------------------------------------------------- /content/welcome/en-US.md: -------------------------------------------------------------------------------- 1 | --- 2 | namespace: others 3 | language: en-US 4 | key: welcome_markdown 5 | --- 6 | 7 | Good day and welcome to poi {{version}}! Before your use, here is some information for you! 8 | 9 | **poi will never modify your game data, but please use trusted releases and plugins!** 10 | 11 | poi does not use proxy by default, and you could change in settings panel: 12 | 13 | - If you're using HTTP or Socks5 proxies, make sure it complies to your proxy information; 14 | - If you're using VPN, simply leave it unset; 15 | - You may also consider enable Editing DMM Cookie Region Flag setting. 16 | 17 | If you're seeing poi under performance, you may disable some plugins. 18 | 19 | If you prefer alphabetic names to Japanese ones, you may try `Translator` plugin. 20 | 21 | --- 22 | 23 | Like poi? Have suggestions? Interested in contributing? [Please see here](https://github.com/poooi/poi) 24 | -------------------------------------------------------------------------------- /views/env-parts/devtool-message.es: -------------------------------------------------------------------------------- 1 | /* global POI_VERSION, getStore, isMain */ 2 | import * as remote from '@electron/remote' 3 | 4 | if (isMain) { 5 | remote.getCurrentWebContents().addListener('devtools-opened', () => { 6 | const PLUGINS = getStore('plugins') || [] 7 | const FCD = getStore('fcd.version') || {} 8 | 9 | const pluginMessage = PLUGINS.filter((plugin) => plugin.enabled) 10 | .map((plugin) => `${plugin.id}@${plugin.version}`) 11 | .join(', ') 12 | 13 | const fcdMessage = Object.keys(FCD) 14 | .map((key) => `${key}@${FCD[key]}`) 15 | .join(', ') 16 | 17 | // eslint-disable-next-line no-console 18 | console.log( 19 | `%cThis is poi@${POI_VERSION} on ${process.platform} ${process.arch} with Electron@${process.versions.electron}, 20 | PLUGINS: ${pluginMessage}, 21 | FCD: ${fcdMessage}`, 22 | 'font-size: 120%', 23 | ) 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /assets/js/plugin-preload.js: -------------------------------------------------------------------------------- 1 | const remote = require('@electron/remote') 2 | const ROOT = remote.getGlobal('ROOT') 3 | const MODULE_PATH = remote.getGlobal('MODULE_PATH') 4 | const config = remote.require('./lib/config') 5 | require('@babel/register')(require(`${ROOT}/babel-register.config`)) 6 | const { setAllowedPath } = require(`${ROOT}/lib/module-path`) 7 | 8 | setAllowedPath(MODULE_PATH, ROOT) 9 | 10 | const onZoomChange = (value) => { 11 | remote.getCurrentWebContents().zoomFactor = value 12 | } 13 | 14 | const handleZoom = (path, value) => { 15 | if (path === 'poi.appearance.zoom') { 16 | onZoomChange(value) 17 | } 18 | } 19 | 20 | config.addListener('config.set', handleZoom) 21 | 22 | window.addEventListener('unload', (e) => { 23 | config.removeListener('config.set', handleZoom) 24 | }) 25 | 26 | document.addEventListener('DOMContentLoaded', () => 27 | onZoomChange(config.get('poi.appearance.zoom', 1)), 28 | ) 29 | -------------------------------------------------------------------------------- /views/services/sortie-dangerous-check.es: -------------------------------------------------------------------------------- 1 | import { damagedCheck } from './utils' 2 | import { Trans } from 'react-i18next' 3 | import React from 'react' 4 | 5 | const { getStore, toggleModal } = window 6 | 7 | window.addEventListener('game.response', ({ detail: { path, body, postBody } }) => { 8 | if (path === '/kcsapi/api_req_map/start' || path === '/kcsapi/api_req_map/next') { 9 | // const {$ships, $equips} = getStore('const') || {} 10 | // const {sortieStatus, escapedPos} = getStore('sortie') || {} 11 | // const {fleets, ships, equips} = getStore('info') || {} 12 | const damagedShips = damagedCheck(getStore('const'), getStore('sortie'), getStore('info')) 13 | if (damagedShips.length > 0) { 14 | return toggleModal( 15 | main:Attention!, 16 | <> 17 | {damagedShips.join(' ')} main:is heavily damaged! 18 | , 19 | ) 20 | } 21 | } 22 | }) 23 | -------------------------------------------------------------------------------- /.stylelintrc.js: -------------------------------------------------------------------------------- 1 | const postCssSyntax = require('postcss-syntax') 2 | 3 | module.exports = { 4 | extends: [ 5 | 'stylelint-config-standard', 6 | 'stylelint-config-prettier', 7 | 'stylelint-config-styled-components', 8 | ], 9 | rules: { 10 | 'alpha-value-notation': 'number', 11 | 'selector-type-no-unknown': [ 12 | true, 13 | { 14 | ignoreTypes: ['kan-game', '/^poi-/', 'title-bar', 'webview'], 15 | }, 16 | ], 17 | 'block-no-empty': null, 18 | 'declaration-colon-newline-after': null, 19 | 'value-keyword-case': ['lower', { ignoreKeywords: [/dummyValue/] }], 20 | 'keyframes-name-pattern': null, 21 | 'function-no-unknown': [true, { 'ignoreFunctions': ['-webkit-gradient', 'from', 'to'] }] 22 | }, 23 | overrides: [ 24 | { 25 | files: ['**/*.es', '**/*.tsx'], 26 | customSyntax: postCssSyntax({ 27 | 'styled-components': true, 28 | }) 29 | } 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /views/services/alert.ts: -------------------------------------------------------------------------------- 1 | import { Message, messageInstance } from 'views/components/info/alert' 2 | 3 | const DEFAULT_STICKYFOR = 3 * 1000 // Milliseconds 4 | 5 | function dispatchAlertEvent(value: Message) { 6 | messageInstance.emit(value) 7 | } 8 | 9 | const mkAlert = (type: string, priority: number) => (msg: string, options: Message['options']) => { 10 | const value = { 11 | content: msg, 12 | type, 13 | priority, 14 | stickyFor: DEFAULT_STICKYFOR, 15 | options, 16 | } 17 | dispatchAlertEvent(value) 18 | } 19 | 20 | const log = mkAlert('default', 0) 21 | const success = mkAlert('success', 1) 22 | const warn = mkAlert('warning', 2) 23 | const error = mkAlert('danger', 4) 24 | 25 | // @ts-expect-error backward compatibility 26 | window.log = log 27 | // @ts-expect-error backward compatibility 28 | window.success = success 29 | // @ts-expect-error backward compatibility 30 | window.warn = warn 31 | // @ts-expect-error backward compatibility 32 | window.error = error 33 | -------------------------------------------------------------------------------- /.github/workflows/nodejs.yml: -------------------------------------------------------------------------------- 1 | name: Node CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | test: 7 | name: test 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v4 12 | - name: Use Node.js 13 | uses: actions/setup-node@v4 14 | with: 15 | node-version-file: '.node-version' 16 | - name: Get npm cache directory 17 | id: npm-cache-dir 18 | run: | 19 | echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT 20 | - uses: actions/cache@v4 21 | with: 22 | path: ${{ steps.npm-cache-dir.outputs.dir }} 23 | key: v1-${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} 24 | restore-keys: | 25 | v1-${{ runner.os }}-node- 26 | - name: npm install 27 | run: | 28 | npm ci 29 | - name: lint 30 | run: | 31 | npm run lint 32 | - name: test 33 | run: | 34 | npm test 35 | env: 36 | CI: true 37 | -------------------------------------------------------------------------------- /views/services/device-pixel-ratio-detector.es: -------------------------------------------------------------------------------- 1 | import EventEmitter from 'events' 2 | 3 | export class devicePixelRatioDetector extends EventEmitter { 4 | constructor() { 5 | super() 6 | this.matchMediaMin = window.matchMedia(`screen and (min-resolution: ${devicePixelRatio}dppx)`) 7 | this.matchMediaMax = window.matchMedia(`screen and (max-resolution: ${devicePixelRatio}dppx)`) 8 | this.matchMediaMin.addListener(this.callback) 9 | this.matchMediaMax.addListener(this.callback) 10 | } 11 | callback = (e) => { 12 | this.matchMediaMin.removeListener(this.callback) 13 | this.matchMediaMax.removeListener(this.callback) 14 | this.matchMediaMin = window.matchMedia(`screen and (min-resolution: ${devicePixelRatio}dppx)`) 15 | this.matchMediaMax = window.matchMedia(`screen and (max-resolution: ${devicePixelRatio}dppx)`) 16 | this.matchMediaMin.addListener(this.callback) 17 | this.matchMediaMax.addListener(this.callback) 18 | this.emit('change', devicePixelRatio) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/shortcut.ts: -------------------------------------------------------------------------------- 1 | import { globalShortcut } from 'electron' 2 | import config from './config' 3 | import windowManager from './window' 4 | import dbg from './debug' 5 | 6 | const registerShortcut = (acc: Electron.Accelerator, desc: string, func: () => void) => { 7 | dbg.log(`Registering shortcut: ${acc}\t=> ${desc}`) 8 | try { 9 | globalShortcut.register(acc, func) 10 | return true 11 | } catch (err) { 12 | console.error(`Failed to register shortcut[${acc}]: ${err}`) 13 | return false 14 | } 15 | } 16 | 17 | const registerBossKey = () => { 18 | const accelerator = config.get('poi.shortcut.bosskey', '') 19 | if (accelerator) 20 | if (!registerShortcut(accelerator, 'Boss Key', windowManager.toggleAllWindowsVisibility)) 21 | config.set('poi.shortcut.bosskey', '') 22 | } 23 | 24 | export default { 25 | register: () => { 26 | if (process.platform !== 'darwin') { 27 | registerBossKey() 28 | } 29 | }, 30 | unregister: () => { 31 | globalShortcut.unregisterAll() 32 | }, 33 | } 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 27 | node_modules 28 | 29 | # poi config 30 | /config.cson 31 | 32 | /components 33 | 34 | /assets/themes 35 | 36 | /cache 37 | 38 | *.swp 39 | 40 | # poi build directory 41 | build/download 42 | app_compiled/ 43 | dist/ 44 | 45 | # icon 46 | !build/icon.icns 47 | !build/icon.ico 48 | !build/icon.png 49 | !build/icons 50 | 51 | # F**k you macOS 52 | .DS_store 53 | ._* 54 | 55 | #Editor config 56 | .nova 57 | -------------------------------------------------------------------------------- /i18n/data/en-US.json: -------------------------------------------------------------------------------- 1 | { 2 | "BOSS戰": "Boss battle", 3 | "BOSS戰勝利": "Boss battle victories", 4 | "S勝利": "S-Rank victories", 5 | "作戰": "Battle", 6 | "入渠": "Repair", 7 | "出擊": "Sortie", 8 | "勝利": "Victories", 9 | "廃棄": "Scrapping", 10 | "廃棄 - 中口径主砲": "Scrapping - Medium Caliber Main Gun", 11 | "廃棄 - 大口径主砲": "Scrapping - Large Caliber Main Gun", 12 | "廃棄 - 副砲": "Scrapping - Secondary Gun", 13 | "廃棄 - ドラム缶(輸送用)": "Scrapping - Drum Canister (Transport Use)", 14 | "廃棄 - 水上偵察機": "Scrapping - Reconnaissance Seaplan", 15 | "廃棄 - 魚雷": "Scrapping - Torpedo", 16 | "建造": "Construction", 17 | "改修": "Equipment improvement", 18 | "敵潜水艦": "Enemy submarines sunk", 19 | "敵空母": "Enemy carriers sunk", 20 | "敵補給艦": "Enemy transport ships sunk", 21 | "演習": "Exercise", 22 | "演習勝利": "Exercise victories", 23 | "演習勝利 A": "Exercise victories (A)", 24 | "演習勝利 S": "Exercise victories (S)", 25 | "補給": "Resupply", 26 | "解体": "Dismantle", 27 | "近代化改修": "Modernization", 28 | "遠征": "Expedition", 29 | "開発": "Development" 30 | } 31 | -------------------------------------------------------------------------------- /views/components/settings/main/index.es: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { withNamespaces } from 'react-i18next' 3 | import { connect } from 'react-redux' 4 | import { get } from 'lodash' 5 | 6 | import { NavigatorBar } from './navigator-bar' 7 | import { StorageConfig } from './storage-config' 8 | import { LanguageConfig } from './language-config' 9 | import { ScreenshotConfig } from './screenshot-config' 10 | import { PluginConfig } from './plugin-config' 11 | import { AdvancedConfig } from './advanced-config' 12 | import { AccessibilityConfig } from './accessibility-config' 13 | 14 | export const PoiConfig = connect((state) => ({ 15 | refts: get(state, 'layout.webview.refts', 0), 16 | }))( 17 | withNamespaces(['setting'])(({ refts, t }) => ( 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | )), 28 | ) 29 | -------------------------------------------------------------------------------- /views/components/settings/components/integer.es: -------------------------------------------------------------------------------- 1 | /* global config */ 2 | import React, { PureComponent } from 'react' 3 | import PropTypes from 'prop-types' 4 | import { connect } from 'react-redux' 5 | import { get, debounce } from 'lodash' 6 | import { NumericInput } from '@blueprintjs/core' 7 | 8 | @connect((state, props) => ({ 9 | value: get(state.config, props.configName, props.defaultValue || 0), 10 | configName: props.configName, 11 | label: props.label, 12 | })) 13 | export class IntegerConfig extends PureComponent { 14 | static propTypes = { 15 | configName: PropTypes.string.isRequired, 16 | value: PropTypes.number.isRequired, 17 | disabled: PropTypes.bool, 18 | defaultValue: PropTypes.number, 19 | } 20 | 21 | handleChange = debounce((value) => { 22 | config.set(this.props.configName, Math.round(value)) 23 | }, 200) 24 | 25 | render() { 26 | const { value, configName, defaultValue, dispatch, ...rest } = this.props 27 | return 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /i18n/menu/zh-CN.json: -------------------------------------------------------------------------------- 1 | { 2 | "Preferences": "设置...", 3 | "Services": "服务", 4 | "About poi": "关于poi", 5 | "Hide poi": "隐藏poi", 6 | "Show poi": "显示poi", 7 | "Hide others": "隐藏其他", 8 | "Show All": "全部显示", 9 | "Always on top": "窗口置顶", 10 | "Resizable": "允许变更窗口大小", 11 | "Reset Views": "重置视窗和分辨率", 12 | "Confirm before exit": "关闭前确认", 13 | "Quit poi": "退出poi", 14 | "Edit": "编辑", 15 | "Undo": "撤销", 16 | "Redo": "重做", 17 | "Cut": "剪切", 18 | "Copy": "复制", 19 | "Paste": "粘贴", 20 | "Select All": "全选", 21 | "View": "显示", 22 | "Reload": "刷新", 23 | "Stop": "停止", 24 | "Developer Tools": "开发者工具", 25 | "Developer Tools of WebView": "Webview 开发者工具", 26 | "Themes": "主题", 27 | "Apply Theme": "设置主题", 28 | "Next Theme": "下一个主题", 29 | "Previous Theme": "前一个主题", 30 | "Window": "窗口", 31 | "Minimize": "最小化", 32 | "Close": "关闭", 33 | "Bring All to Front": "全部置前", 34 | "Help": "帮助", 35 | "Wiki": "Wiki" , 36 | "poi Statistics": "poi 掉落统计", 37 | "Report Issue": "报告问题", 38 | "Search Issues": "搜索问题", 39 | "Enable Vibrance": "半透明主题" 40 | } 41 | -------------------------------------------------------------------------------- /i18n/menu/zh-TW.json: -------------------------------------------------------------------------------- 1 | { 2 | "Preferences": "設定...", 3 | "Services": "服務", 4 | "About poi": "關於poi", 5 | "Hide poi": "隱藏poi", 6 | "Show poi": "顯示poi", 7 | "Hide others": "隱藏其他", 8 | "Show All": "全部顯示", 9 | "Always on top": "視窗置頂", 10 | "Reset Views": "重置視窗和分辨率", 11 | "Resizable": "允許變更視窗大小", 12 | "Confirm before exit": "關閉前確認", 13 | "Quit poi": "退出poi", 14 | "Edit": "編輯", 15 | "Undo": "撤銷", 16 | "Redo": "重做", 17 | "Cut": "剪下", 18 | "Copy": "複製", 19 | "Paste": "貼上", 20 | "Select All": "全選", 21 | "View": "顯示", 22 | "Reload": "重新整理", 23 | "Stop": "停止", 24 | "Developer Tools": "開發者工具", 25 | "Developer Tools of WebView": "Webview 開發者工具", 26 | "Themes": "主題", 27 | "Apply Theme": "設定主題", 28 | "Next Theme": "下一個主題", 29 | "Previous Theme": "前一個主題", 30 | "Window": "視窗", 31 | "Minimize": "最小化", 32 | "Close": "關閉", 33 | "Bring All to Front": "全部置前", 34 | "Help": "幫助", 35 | "Wiki": "Wiki" , 36 | "poi Statistics": "poi 掉落統計", 37 | "Report Issue": "報告問題", 38 | "Search Issues": "搜尋問題", 39 | "Enable Vibrance": "半透明主題" 40 | } 41 | -------------------------------------------------------------------------------- /views/utils/notifiers.es: -------------------------------------------------------------------------------- 1 | import notifCenter from 'views/env-parts/notif-center' 2 | 3 | // Notification that supports equalKey. 4 | // completeTime: unix ms. 5 | // preemptTime: seconds. 6 | export class CountdownNotifier { 7 | constructor() { 8 | this._lastCompleteTime = null 9 | // Two notif must be separated by at least one non-notif call to tryNotify 10 | this._justNotified = false 11 | } 12 | tryNotify = (o) => { 13 | if ( 14 | o.completeTime != null && 15 | this._lastCompleteTime != null && 16 | o.completeTime === this._lastCompleteTime 17 | ) { 18 | return 19 | } 20 | // 1 second more to preemptTime bc notifications are throttled by 1 sec 21 | const preemptTime = Math.max((o.preemptTime || 0) + 1, 0) 22 | if (o.completeTime <= Date.now() + preemptTime * 1000) { 23 | this._lastCompleteTime = o.completeTime 24 | if (!this._justNotified) { 25 | notifCenter.notify(o) 26 | this._justNotified = true 27 | } 28 | } else { 29 | this._justNotified = false 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /views/utils/event-emitter.ts: -------------------------------------------------------------------------------- 1 | export interface Listener { 2 | (event: T): void 3 | } 4 | 5 | export interface Disposable { 6 | dispose(): void 7 | } 8 | 9 | export class EventEmitter { 10 | private listeners: Listener[] = [] 11 | private listenersOncer: Listener[] = [] 12 | 13 | on = (listener: Listener): Disposable => { 14 | this.listeners.push(listener) 15 | return { 16 | dispose: () => this.off(listener), 17 | } 18 | } 19 | 20 | once = (listener: Listener): void => { 21 | this.listenersOncer.push(listener) 22 | } 23 | 24 | off = (listener: Listener) => { 25 | const callbackIndex = this.listeners.indexOf(listener) 26 | if (callbackIndex > -1) this.listeners.splice(callbackIndex, 1) 27 | } 28 | 29 | emit = (event: T) => { 30 | this.listeners.forEach((listener) => listener(event)) 31 | 32 | this.listenersOncer.forEach((listener) => listener(event)) 33 | this.listenersOncer = [] 34 | } 35 | 36 | pipe = (te: EventEmitter): Disposable => { 37 | return this.on((e) => te.emit(e)) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /assets/js/capture-page.js: -------------------------------------------------------------------------------- 1 | window.capture = async function () { 2 | try { 3 | const canvas = document.querySelector('#game_frame') 4 | ? document 5 | .querySelector('#game_frame') 6 | .contentDocument.querySelector('#htmlWrap') 7 | .contentDocument.querySelector('canvas') 8 | : document.querySelector('#htmlWrap') 9 | ? document.querySelector('#htmlWrap').contentDocument.querySelector('canvas') 10 | : document.querySelector('canvas') 11 | ? document.querySelector('canvas') 12 | : null 13 | if (!canvas || !ImageCapture) return undefined 14 | const imageCapture = new ImageCapture(canvas.captureStream(0).getVideoTracks()[0]) 15 | const imageBitmap = await imageCapture.grabFrame() 16 | const tempCanvas = document.createElement('canvas') 17 | tempCanvas.width = imageBitmap.width 18 | tempCanvas.height = imageBitmap.height 19 | tempCanvas.getContext('2d').drawImage(imageBitmap, 0, 0) 20 | return tempCanvas.toDataURL() 21 | } catch (e) { 22 | console.error(e) 23 | return undefined 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /views/services/sortie-expedition-resupply-check.es: -------------------------------------------------------------------------------- 1 | /* global config, getStore */ 2 | import i18next from 'views/env-parts/i18next' 3 | 4 | window.addEventListener('game.response', ({ detail: { path } }) => { 5 | if (!config.get('poi.expeditionResupplyCheck.enable', false)) return 6 | if (path !== '/kcsapi/api_get_member/mission') return 7 | 8 | const fleets = getStore('info.fleets') 9 | const ships = getStore('info.ships') 10 | const $ships = getStore('const.$ships') 11 | const needResupply = fleets 12 | .filter((_, index) => index !== 0) 13 | .flatMap((fleet) => fleet.api_ship) 14 | .some((shipId) => { 15 | const ship = ships[shipId] 16 | const $ship = $ships[ship?.api_ship_id] 17 | if (!ship || !$ship) return false 18 | return ship.api_bull < $ship.api_bull_max || ship.api_fuel < $ship.api_fuel_max 19 | }) 20 | 21 | if (needResupply) { 22 | window.toast(i18next.t('main:At least one fleet has not been fully resupplied'), { 23 | type: 'warning', 24 | title: i18next.t('main:Expedition resupply warning'), 25 | }) 26 | } 27 | }) 28 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | [ 4 | require.resolve('@babel/preset-env'), 5 | { 6 | targets: { 7 | electron: '36.3', 8 | }, 9 | loose: true, 10 | }, 11 | ], 12 | require.resolve('@babel/preset-react'), 13 | require.resolve('@babel/preset-typescript'), 14 | ], 15 | plugins: [ 16 | require.resolve('babel-plugin-styled-components'), 17 | [require.resolve('@babel/plugin-proposal-decorators'), { legacy: true }], 18 | [require.resolve('@babel/plugin-proposal-pipeline-operator'), { proposal: 'minimal' }], 19 | ].concat( 20 | [ 21 | '@babel/plugin-proposal-do-expressions', 22 | '@babel/plugin-proposal-export-default-from', 23 | '@babel/plugin-proposal-function-bind', 24 | '@babel/plugin-proposal-function-sent', 25 | '@babel/plugin-proposal-throw-expressions', 26 | 'babel-plugin-add-module-exports', 27 | 'babel-plugin-dynamic-import-node', 28 | ].map((plugin) => require.resolve(plugin)), 29 | ), 30 | ignore: [], 31 | only: [/\.(es|ts|tsx)$/], 32 | babelrc: false, 33 | } 34 | -------------------------------------------------------------------------------- /i18n/menu/ko-KR.json: -------------------------------------------------------------------------------- 1 | { 2 | "Preferences": "환경설정", 3 | "Services": "서비스", 4 | "About poi": "poi?", 5 | "Hide poi": "poi 숨기기", 6 | "Show poi": "poi 표시", 7 | "Hide others": "기타 항목 숨기기", 8 | "Show All": "모두 표시", 9 | "Always on top": "창을 항상 맨 위에 표시", 10 | "Resizable": "창 크기 변경 허용", 11 | "Reset Views": "창 재설정", 12 | "Confirm before exit": "종료하기 전에 경고 메시지 표시", 13 | "Quit poi": "poi 종료", 14 | "Edit": "편집", 15 | "Undo": "실행 취소", 16 | "Redo": "다시 실행", 17 | "Cut": "잘라내기", 18 | "Copy": "복사", 19 | "Paste": "붙여넣기", 20 | "Select All": "모두 선택", 21 | "View": "보기", 22 | "Reload": "재시작", 23 | "Stop": "중지", 24 | "Developer Tools": "개발자 도구", 25 | "Developer Tools of WebView": "WebView의 개발자 도구", 26 | "Themes": "테마", 27 | "Apply Theme": "테마 적용", 28 | "Next Theme": "다음 테마", 29 | "Previous Theme": "이전 테마", 30 | "Window": "창", 31 | "Minimize": "최소화", 32 | "Close": "닫기", 33 | "Bring All to Front": "모든 창 앞으로 가져오기", 34 | "Help": "도움말", 35 | "Wiki": "위키", 36 | "poi Statistics": "poi 통계", 37 | "Report Issue": "이슈 보고", 38 | "Search Issues": "이슈 검색", 39 | "Enable Vibrance": "Vibrance 적용" 40 | } 41 | -------------------------------------------------------------------------------- /assets/data/fcd/shiptag.json: -------------------------------------------------------------------------------- 1 | {"meta":{"name":"shiptag","version":"2025/11/09/01"},"data":{"mapname":["改R4計画艦隊","ナルヴィク先遣隊","ナルヴィク防衛主隊","ナルヴィク駐留艦隊","イギリス本国艦隊","イギリス機動部隊","欧州遠征収容艦隊","拡張第三十一戦隊","機動部隊","礼号作戦部隊","横須賀防備戦隊","連合艦隊","第百四戦隊","決戦艦隊"],"color":["#d1d1d1","#d2c34d","#a1cd87","#cbb6f6","#49bfc7","#f59e2b","#c2b9ad","#e199be","#b0f75f","#fee75d","#41c5f7","#58f97d","#57a5ff","#4ebc7b"],"fleetname":{"zh-CN":["改R4计划舰队","纳尔维克先遣队","纳尔维克防卫主队","纳尔维克驻留舰队","英国本国舰队","英国机动部队","欧洲远征收容舰队","扩张第三十一战队","机动部队","礼号作战部队","横须贺防备战队","连合舰队","第百四战队","决战舰队"],"zh-TW":["改R4計畫艦隊","納爾維克先遣隊","納爾維克防衛主隊","納爾維克駐留艦隊","英國本國艦隊","英國機動部隊","歐洲遠征收容艦隊","擴張第三十一戰隊","機動部隊","禮號作戰部隊","橫須賀防備戰隊","連合艦隊","第百四戰隊","決戰艦隊"],"ja-JP":["改R4計画艦隊","ナルヴィク先遣隊","ナルヴィク防衛主隊","ナルヴィク駐留艦隊","イギリス本国艦隊","イギリス機動部隊","欧州遠征収容艦隊","拡張第三十一戦隊","機動部隊","礼号作戦部隊","横須賀防備戦隊","連合艦隊","第百四戦隊","決戦艦隊"],"en-US":["Plan R4 Kai Fleet","Narvik Advance Force","Narvik Main Defense Force","Narvik Garrison Fleet","British Home Fleet","British Carrier Task Force","European Expeditionary Fleet","Expanded 31st Squadron","Carrier Task Force","Operation Rei-go Fleet","Yokosuka Defense Squadron","Combined Fleet","104th Squadron","Decisive Battle Fleet"]}}} 2 | -------------------------------------------------------------------------------- /views/utils/__tests__/aaci.spec.es: -------------------------------------------------------------------------------- 1 | import { every, isFinite, each, isArray, isString, isBoolean } from 'lodash' 2 | import { AACITable, getShipAACIs } from '../aaci' 3 | 4 | const { ship, equips } = require('./fixtures/aaci-sample-ship.json') 5 | 6 | const isStringArray = (array) => isArray(array) && every(array, (e) => isString(e)) 7 | 8 | describe('AACI entry check', () => { 9 | it('AACI key is numeric', () => { 10 | expect(Object.keys(AACITable).length > 0).toBe(true) 11 | expect(every(Object.keys(AACITable), (key) => isFinite(+key))).toBe(true) 12 | }) 13 | 14 | it('AACI entry should be valid', () => { 15 | each(AACITable, ({ name, id, fixed, modifier, shipValid, equipsValid }) => { 16 | expect(name === '' || isStringArray(name)).toBe(true) 17 | expect(isFinite(id) && id > 0).toBe(true) 18 | expect(isFinite(modifier) && modifier > 0).toBe(true) 19 | expect(isBoolean(shipValid(ship))) 20 | expect(isBoolean(equipsValid(equips))).toBe(true) 21 | }) 22 | }) 23 | 24 | it('sample ship should match aaci test', () => { 25 | expect(getShipAACIs(ship, equips).length > 0).toBe(true) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2021 poi contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /i18n/menu/ja-JP.json: -------------------------------------------------------------------------------- 1 | { 2 | "Preferences": "環境設定...", 3 | "Services": "サービス", 4 | "About poi": "poiについて", 5 | "Hide poi": "poiを隠す", 6 | "Show poi": "poiを表示", 7 | "Hide others": "ほかを隠す", 8 | "Show All": "すべて表示", 9 | "Always on top": "ウィンドウを常に手前に表示する", 10 | "Resizable": "ウィンドウサイズの変更を許可", 11 | "Reset Views": "ウィンドウサイズをリセット", 12 | "Confirm before exit": "終了する前に警告メッセージを表示する", 13 | "Quit poi": "poiを終了", 14 | "Edit": "編集", 15 | "Undo": "取り消す", 16 | "Redo": "やり直す", 17 | "Cut": "切り取り", 18 | "Copy": "コピー", 19 | "Paste": "貼り付け", 20 | "Select All": "すべてを選択", 21 | "View": "表示", 22 | "Reload": "再読み込み", 23 | "Stop": "停止する", 24 | "Developer Tools": "デベロッパーツール", 25 | "Developer Tools of WebView": "Webviewのデベロッパーツール", 26 | "Themes": "テーマ", 27 | "Apply Theme": "テーマを設定", 28 | "Next Theme": "次のテーマ", 29 | "Previous Theme": "前のテーマ", 30 | "Window": "ウインドウ", 31 | "Minimize": "最小化", 32 | "Close": "閉じる", 33 | "Bring All to Front": "すべてを手前に移動", 34 | "Help": "ヘルプ", 35 | "Wiki": "ウィキ" , 36 | "poi Statistics": "poi 統計データベースツール", 37 | "Report Issue": "問題の報告", 38 | "Search Issues": "問題を検索", 39 | "Enable Vibrance": "バイブランス" 40 | } 41 | -------------------------------------------------------------------------------- /views/components/etc/context-button-tooltip.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Generate tooltip for buttons that supports context menu user interaction 3 | */ 4 | import React from 'react' 5 | import { useTranslation } from 'react-i18next' 6 | import { 7 | InfoTooltip, 8 | InfoTooltipEntry, 9 | InfoTooltipItem, 10 | } from 'views/components/etc/styled-components' 11 | 12 | interface ContextButtonTooltipProps { 13 | left: string 14 | right: string 15 | } 16 | 17 | const ContextButtonTooltip: React.FC = ({ left, right }) => { 18 | const { t } = useTranslation('setting') 19 | 20 | return ( 21 | 22 | 23 | {t('Left click')} 24 | {left} 25 | 26 | 27 | {t('Right click')} 28 | {right} 29 | 30 | 31 | ) 32 | } 33 | 34 | export default ContextButtonTooltip 35 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 代码缺陷汇报 3 | about: Create a report to help us improve 汇报程序代码错误 4 | --- 5 | 6 | 10 | 11 | **poi 版本 / poi version:** 12 | 13 | **操作系统 / OS:** 14 | 15 | 19 | 20 | **插件名和版本 / Plugin name & version:** 21 | 22 | 26 | 27 | **你遇到了什么样的问题 / The problem you've met:** 28 | 29 | **有没有重现的方法,或者与问题相关的任何信息 / How to reproduce, or any information that might be related:** 30 | 31 | 37 | 38 | 40 | -------------------------------------------------------------------------------- /views/components/settings/gaming/pre-sortie-config/index.es: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { withNamespaces } from 'react-i18next' 3 | import { FormGroup } from '@blueprintjs/core' 4 | 5 | import { SwitchConfig } from 'views/components/settings/components/switch' 6 | import { Section } from 'views/components/settings/components/section' 7 | import { SlotCheckConfig } from './slot-check-config' 8 | import { UnusedSlotCheckConfig } from './unused-slot-check-config' 9 | 10 | export const PreSortieConfig = withNamespaces(['setting'])(({ t }) => ( 11 |
12 | 13 | 14 | 15 | 20 | 21 | 22 | 27 | 28 | 29 |
30 | )) 31 | -------------------------------------------------------------------------------- /lib/__tests__/__snapshots__/config.spec.es.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`config initially, should be filled with default value 1`] = `Array []`; 4 | 5 | exports[`config initially, should be filled with default value 2`] = ` 6 | Object { 7 | "foo": "bar", 8 | } 9 | `; 10 | 11 | exports[`config should be set and get a object correctly 1`] = ` 12 | Array [ 13 | Array [ 14 | Object { 15 | "another": Object { 16 | "a": "b", 17 | "c": 187, 18 | }, 19 | "foo": "bar", 20 | "path": Object { 21 | "to": Object { 22 | "value": "Dead Beef", 23 | }, 24 | }, 25 | }, 26 | undefined, 27 | 2, 28 | ], 29 | ] 30 | `; 31 | 32 | exports[`config should be set and get a object correctly 2`] = `"CSON Stringified"`; 33 | 34 | exports[`config should be set and get a string correctly 1`] = ` 35 | Array [ 36 | Array [ 37 | Object { 38 | "foo": "bar", 39 | "path": Object { 40 | "to": Object { 41 | "value": "Dead Beef", 42 | }, 43 | }, 44 | }, 45 | undefined, 46 | 2, 47 | ], 48 | ] 49 | `; 50 | 51 | exports[`config should be set and get a string correctly 2`] = `"CSON Stringified"`; 52 | -------------------------------------------------------------------------------- /views/components/settings/components/switch.es: -------------------------------------------------------------------------------- 1 | /* global config */ 2 | import React, { Component } from 'react' 3 | import PropTypes from 'prop-types' 4 | import { connect } from 'react-redux' 5 | import { get } from 'lodash' 6 | import { Switch } from '@blueprintjs/core' 7 | import { styled } from 'styled-components' 8 | 9 | const SwitchWithMargin = styled(Switch)` 10 | margin-right: 8px; 11 | ` 12 | 13 | @connect((state, props) => ({ 14 | value: get(state.config, props.configName, props.defaultValue), 15 | configName: props.configName, 16 | label: props.label, 17 | })) 18 | export class SwitchConfig extends Component { 19 | static propTypes = { 20 | label: PropTypes.node.isRequired, 21 | configName: PropTypes.string.isRequired, 22 | value: PropTypes.bool.isRequired, 23 | disabled: PropTypes.bool, 24 | defaultValue: PropTypes.bool, 25 | } 26 | 27 | handleChange = () => { 28 | config.set(this.props.configName, !this.props.value) 29 | } 30 | 31 | render() { 32 | const { value, configName, label, defaultValue, dispatch, ...rest } = this.props 33 | return ( 34 | 35 | {label} 36 | 37 | ) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /i18n/data/zh-CN.json: -------------------------------------------------------------------------------- 1 | { 2 | "HP": "耐久", 3 | "Armor": "装甲", 4 | "Firepower": "火力", 5 | "Torpedo": "雷装", 6 | "Speed": "速力", 7 | "Bombing": "爆装", 8 | "AA": "对空", 9 | "ASW": "对潜", 10 | "Accuracy": "命中", 11 | "Torpedo Accuracy": "雷撃命中", 12 | "Evasion": "回避", 13 | "Torpedo Evasion": "雷撃回避", 14 | "Bombing Evasion": "爆撃回避", 15 | "LOS": "索敵", 16 | "Anti-LOS": "索敵妨害", 17 | "Luck": "運", 18 | "Range": "射程", 19 | "Short": "短", 20 | "Medium": "中", 21 | "Long": "長", 22 | "Very Long": "超長", 23 | "BOSS戰": "BOSS战", 24 | "BOSS戰勝利": "BOSS战胜利", 25 | "S勝利": "S胜利", 26 | "作戰": "作战", 27 | "入渠": "入渠", 28 | "出擊": "出击", 29 | "勝利": "胜利", 30 | "廃棄": "废弃", 31 | "廃棄 - 中口径主砲": "废弃 - 中口径主炮", 32 | "廃棄 - 大口径主砲": "废弃 - 大口径主炮", 33 | "廃棄 - 副砲": "废弃 - 副炮", 34 | "廃棄 - ドラム缶(輸送用)": "废弃 - 鼓桶(运输用)", 35 | "廃棄 - 水上偵察機": "废弃 - 水上侦察机", 36 | "廃棄 - 魚雷": "废弃 - 鱼雷", 37 | "建造": "建造", 38 | "改修": "改修", 39 | "敵潜水艦": "敌潜水舰", 40 | "敵空母": "敌空母", 41 | "敵補給艦": "敌补给舰", 42 | "演習": "演习", 43 | "演習勝利": "演習勝利", 44 | "演習勝利 A": "演習勝利 A", 45 | "演習勝利 S": "演習勝利 S", 46 | "補給": "补给", 47 | "解体": "解体", 48 | "近代化改修": "近代化改修", 49 | "遠征": "远征", 50 | "開発": "开发", 51 | "Interception": "迎击", 52 | "Anti-Bomber": "对爆", 53 | "Lv": "等级", 54 | "FP": "火力" 55 | } 56 | -------------------------------------------------------------------------------- /i18n/data/zh-TW.json: -------------------------------------------------------------------------------- 1 | { 2 | "HP": "耐久", 3 | "Armor": "裝甲", 4 | "Firepower": "火力", 5 | "Torpedo": "雷裝", 6 | "Speed": "速力", 7 | "Bombing": "爆裝", 8 | "AA": "對空", 9 | "ASW": "對潛", 10 | "Accuracy": "命中", 11 | "Torpedo Accuracy": "雷撃命中", 12 | "Evasion": "迴避", 13 | "Torpedo Evasion": "雷撃迴避", 14 | "Bombing Evasion": "爆撃迴避", 15 | "LOS": "索敵", 16 | "Anti-LOS": "索敵妨害", 17 | "Luck": "運", 18 | "Range": "射程", 19 | "Short": "短", 20 | "Medium": "中", 21 | "Long": "長", 22 | "Very Long": "超長", 23 | "BOSS戰": "BOSS戰", 24 | "BOSS戰勝利": "BOSS戰勝利", 25 | "S勝利": "S勝利", 26 | "作戰": "作戰", 27 | "入渠": "入渠", 28 | "出擊": "出擊", 29 | "勝利": "勝利", 30 | "廃棄": "廢棄", 31 | "廃棄 - 中口径主砲": "廢棄 - 中口径主炮", 32 | "廃棄 - 大口径主砲": "廢棄 - 大口径主炮", 33 | "廃棄 - 副砲": "廢棄 - 副炮", 34 | "廃棄 - ドラム缶(輸送用)": "廢棄 - 鼓桶(運輸用)", 35 | "廃棄 - 水上偵察機": "廢棄 - 水上偵察機", 36 | "廃棄 - 魚雷": "廢棄 - 魚雷", 37 | "建造": "建造", 38 | "改修": "改修", 39 | "敵潛水艦": "敵潛水艦", 40 | "敵空母": "敵空母", 41 | "敵補給艦": "敵補給艦", 42 | "演習": "演習", 43 | "演習勝利": "演習勝利", 44 | "演習勝利 A": "演習勝利 A", 45 | "演習勝利 S": "演習勝利 S", 46 | "補給": "補給", 47 | "解体": "解體", 48 | "近代化改修": "近代化改修", 49 | "遠征": "遠征", 50 | "開発": "開發", 51 | "Interception": "迎擊", 52 | "Anti-Bomber": "對爆", 53 | "Lv": "等級", 54 | "FP": "火力" 55 | } 56 | -------------------------------------------------------------------------------- /content/merge-content.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This script is for appending markdown content to i18n files 3 | * usage: node merge-content.js 4 | */ 5 | 6 | require('@babel/register')(require('../babel-register.config')) 7 | const glob = require('glob') 8 | const matter = require('gray-matter') 9 | const Promise = require('bluebird') 10 | const path = require('path') 11 | const fs = require('fs-extra') 12 | const _ = require('lodash') 13 | const assert = require('assert').strict 14 | const { compareUpdate } = require('../views/utils/tools') 15 | 16 | const main = async () => { 17 | const data = {} 18 | 19 | await Promise.map(glob.sync(path.resolve(__dirname, '**/*.md')), async (file) => { 20 | const text = await fs.readFile(file, 'utf8') 21 | 22 | const { 23 | content, 24 | data: { namespace, language, key }, 25 | } = matter(text) 26 | _.each([content, namespace, language, key], (v) => assert(v)) 27 | _.set(data, [`${namespace}/${language}`, key], content) 28 | }) 29 | 30 | await Promise.each(Object.keys(data), async (nl) => { 31 | const file = path.resolve(__dirname, `../i18n/${nl}.json`) 32 | const content = await fs.readJSON(file) 33 | return fs.writeJSON(file, compareUpdate(content, data[nl]), { spaces: 2 }) 34 | }) 35 | } 36 | 37 | main() 38 | -------------------------------------------------------------------------------- /views/utils/file-writer.ts: -------------------------------------------------------------------------------- 1 | import { writeFile, ensureDir, WriteFileOptions } from 'fs-extra' 2 | import { dirname } from 'path' 3 | 4 | // A stream of async file writing. `write` queues the task which will be executed 5 | // after all tasks before are done. 6 | // Every instance contains an independent queue. 7 | // Usage: 8 | // var fw = new FileWriter(); 9 | // var path = '/path/to/a/file'; 10 | // for (var i = 0; i < 100; i++) { 11 | // fw.write(path, (''+i).repeat(10000)); 12 | // } 13 | export default class FileWriter { 14 | private writing: boolean 15 | private _queue: Array<[string, string, WriteFileOptions | BufferEncoding | string]> 16 | 17 | constructor() { 18 | this.writing = false 19 | this._queue = [] 20 | } 21 | 22 | write(path: string, data: string, options: WriteFileOptions | BufferEncoding | string) { 23 | this._queue.push([path, data, options]) 24 | this._continueWriting() 25 | } 26 | 27 | private async _continueWriting() { 28 | if (this.writing) return 29 | this.writing = true 30 | while (this._queue.length > 0) { 31 | const [path, data, options] = this._queue.shift() || ['', '', ''] 32 | await ensureDir(dirname(path)) 33 | await writeFile(path, data, options) 34 | } 35 | this.writing = false 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | 8 | 9 | **poi 版本 / poi version:** 10 | 11 | **操作系统 / OS:** 12 | 13 | 17 | 18 | **插件名和版本 / Plugin name & version:** 19 | 20 | 24 | 25 | **你遇到了什么样的问题 / The problem you've met:** 26 | 27 | **有没有重现的方法,或者与问题相关的任何信息 / How to reproduce, or any information that might be related:** 28 | 29 | 35 | 36 | 38 | -------------------------------------------------------------------------------- /views/redux/const.es: -------------------------------------------------------------------------------- 1 | import { indexify } from 'views/utils/tools' 2 | import { keyBy } from 'lodash' 3 | 4 | function dataFromBody(body) { 5 | return { 6 | $ships: indexify(body.api_mst_ship), 7 | $shipTypes: indexify(body.api_mst_stype), 8 | $equips: indexify(body.api_mst_slotitem), 9 | $equipTypes: indexify(body.api_mst_slotitem_equiptype), 10 | $mapareas: indexify(body.api_mst_maparea), 11 | $maps: indexify(body.api_mst_mapinfo), 12 | $missions: indexify(body.api_mst_mission), 13 | $useitems: indexify(body.api_mst_useitem), 14 | $graphs: indexify(body.api_mst_shipgraph), 15 | $shipgraph: body.api_mst_shipgraph, // FIXME: finally deprecate $shipgraph in favor of $graphs 16 | /* 17 | IMPORTANT: do not indexify api_mst_shipupgrade, 18 | because api_id does not suggest uniqueness in this part 19 | due to having cyclic remodel chains. 20 | */ 21 | $shipUpgrades: body.api_mst_shipupgrade, 22 | $exslotEquips: body.api_mst_equip_exslot, 23 | $exslotEquipShips: keyBy(body.api_mst_equip_exslot_ship, 'api_slotitem_id'), 24 | } 25 | } 26 | 27 | export function reducer(state = {}, { type, body }) { 28 | switch (type) { 29 | case '@@Response/kcsapi/api_start2/getData': 30 | return dataFromBody(body) 31 | } 32 | return state 33 | } 34 | -------------------------------------------------------------------------------- /lib/tray.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-namespace */ 2 | import path from 'path' 3 | import { app, Tray, systemPreferences, nativeTheme } from 'electron' 4 | 5 | declare global { 6 | namespace NodeJS { 7 | interface Global { 8 | appTray: Tray 9 | } 10 | } 11 | } 12 | 13 | const getIcon = (platform: NodeJS.Platform) => { 14 | if (platform === 'linux') { 15 | return 'poi_32x32.png' 16 | } 17 | if (platform === 'darwin') { 18 | if (nativeTheme.shouldUseDarkColors) { 19 | return 'poi_ribbon_dark.png' 20 | } 21 | return 'poi_ribbon_light.png' 22 | } 23 | return 'poi.ico' 24 | } 25 | 26 | const getIconPath = (platform: NodeJS.Platform) => 27 | path.join(global.ROOT, 'assets', 'icons', getIcon(platform)) 28 | 29 | let tray: Tray | null = null 30 | app.on('ready', () => { 31 | tray = new Tray(getIconPath(process.platform)) 32 | global.appTray = tray 33 | tray.on('click', () => { 34 | if (global.mainWindow?.isMinimized()) { 35 | global.mainWindow.restore() 36 | } else { 37 | global.mainWindow?.show() 38 | } 39 | }) 40 | tray.setToolTip(app.getName()) 41 | }) 42 | 43 | if (process.platform === 'darwin') { 44 | systemPreferences.subscribeNotification('AppleInterfaceThemeChangedNotification', () => { 45 | tray?.setImage(getIconPath(process.platform)) 46 | }) 47 | } 48 | -------------------------------------------------------------------------------- /views/services/sortie-unused-slot-check.es: -------------------------------------------------------------------------------- 1 | /* global getStore, config */ 2 | import { fleetStateSelectorFactory } from '../utils/selectors' 3 | import i18next from 'views/env-parts/i18next' 4 | import { get } from 'lodash' 5 | 6 | window.addEventListener('game.response', ({ detail: { path, body } }) => { 7 | if (path !== '/kcsapi/api_get_member/mapinfo') return 8 | if (!config.get('poi.unusedEquipmentSlotCheck.enable', false)) return 9 | 10 | const ignoreUnlocked = config.get('poi.unusedEquipmentSlotCheck.ignoreUnlocked', false) 11 | 12 | const state = getStore() 13 | const fleets = get(state, 'info.fleets') 14 | const ships = get(state, 'info.ships') 15 | 16 | const checkSlot = (ship) => { 17 | if (!ship) { 18 | return false 19 | } 20 | if (ignoreUnlocked && !ship.api_locked) { 21 | return false 22 | } 23 | return ( 24 | ship.api_slot.some((equip, index) => index < ship.api_slotnum && equip === -1) || 25 | ship.api_slot_ex === -1 26 | ) 27 | } 28 | 29 | const flag = fleets 30 | .filter((_, fleetId) => ![3, 4, 5].includes(fleetStateSelectorFactory(fleetId)(state))) 31 | .flatMap((fleet) => fleet.api_ship) 32 | .some((shipId) => checkSlot(ships[shipId])) 33 | 34 | if (flag) { 35 | window.toast(i18next.t('main:Unused equipment slot appears in your fleet'), { 36 | type: 'warning', 37 | title: i18next.t('main:Unused equipment slot warning'), 38 | }) 39 | } 40 | }) 41 | -------------------------------------------------------------------------------- /views/components/settings/components/radio.es: -------------------------------------------------------------------------------- 1 | /* global config */ 2 | import React, { Component } from 'react' 3 | import PropTypes from 'prop-types' 4 | import { connect } from 'react-redux' 5 | import { get, map } from 'lodash' 6 | import { Radio, RadioGroup } from '@blueprintjs/core' 7 | 8 | @connect((state, props) => ({ 9 | value: get(state.config, props.configName, props.defaultValue), 10 | configName: props.configName, 11 | label: props.label, 12 | availableVal: props.availableVal, 13 | })) 14 | export class RadioConfig extends Component { 15 | static propTypes = { 16 | label: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), 17 | configName: PropTypes.string, 18 | value: PropTypes.string, 19 | availableVal: PropTypes.array, 20 | defaultValue: PropTypes.string, 21 | } 22 | 23 | componentDidMount = () => { 24 | if (typeof this.props.defaultVal !== 'undefined') { 25 | console.error('prop `defaultVal` is deprecated, use `defaultValue` instaed') 26 | } 27 | } 28 | 29 | handleChange = (e) => { 30 | config.set(this.props.configName, e.currentTarget.value) 31 | } 32 | 33 | render() { 34 | return ( 35 | 36 | {map(this.props.availableVal, (item) => ( 37 | 38 | {item.name} 39 | 40 | ))} 41 | 42 | ) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /views/components/ship/oasw-indicator.es: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { connect } from 'react-redux' 3 | import { createSelector } from 'reselect' 4 | import { memoize } from 'lodash' 5 | import { Tag, Intent } from '@blueprintjs/core' 6 | 7 | import { shipDataSelectorFactory, shipEquipDataSelectorFactory } from 'views/utils/selectors' 8 | import { isOASW } from 'views/utils/oasw' 9 | import { withNamespaces } from 'react-i18next' 10 | import { ShipLabel } from 'views/components/ship-parts/styled-components' 11 | 12 | const OASWSelectorFactory = memoize((shipId) => 13 | createSelector( 14 | [shipDataSelectorFactory(shipId), shipEquipDataSelectorFactory(shipId)], 15 | ([_ship = {}, $ship = {}] = [], _equips = []) => { 16 | const ship = { ...$ship, ..._ship } 17 | const equips = _equips 18 | .filter(([_equip, $equip, onslot] = []) => !!_equip && !!$equip) 19 | .map(([_equip, $equip, onslot]) => ({ ...$equip, ..._equip })) 20 | 21 | return isOASW(ship, equips) 22 | }, 23 | ), 24 | ) 25 | 26 | export const OASWIndicator = withNamespaces(['main'])( 27 | connect((state, { shipId }) => ({ 28 | isOASW: OASWSelectorFactory(shipId)(state), 29 | }))( 30 | ({ isOASW, shipId, t }) => 31 | isOASW && ( 32 | 33 | 34 | {t('main:OASW')} 35 | 36 | 37 | ), 38 | ), 39 | ) 40 | -------------------------------------------------------------------------------- /views/components/settings/components/section.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * section is a group of settings on certain subject 3 | */ 4 | import React, { ReactNode, FunctionComponent } from 'react' 5 | import PropTypes from 'prop-types' 6 | import { Card, H3 } from '@blueprintjs/core' 7 | import { styled } from 'styled-components' 8 | 9 | interface SectionProps { 10 | title: ReactNode 11 | children: ReactNode 12 | } 13 | 14 | const SectionCard = styled(Card)` 15 | & + & { 16 | margin-top: 0.625em; 17 | } 18 | ` 19 | 20 | export const Section: FunctionComponent = ({ title, children, ...props }) => ( 21 | 22 | {title &&

{title}

} 23 | {children} 24 |
25 | ) 26 | 27 | Section.propTypes = { 28 | title: PropTypes.node, 29 | children: PropTypes.node, 30 | } 31 | 32 | export const Wrapper = styled.div` 33 | display: flex; 34 | flex-wrap: wrap; 35 | width: 100%; 36 | align-items: center; 37 | 38 | .bp3-switch { 39 | margin-right: 2em; 40 | } 41 | 42 | .bp3-form-content { 43 | flex: 1; 44 | } 45 | 46 | .bp3-numeric-input { 47 | display: inline-flex; 48 | } 49 | 50 | .bp3-numeric-input .bp3-input-group { 51 | width: 5em; 52 | } 53 | 54 | .bp3-callout { 55 | font-size: 12px; 56 | } 57 | ` 58 | 59 | export const FillAvailable = styled(Wrapper)` 60 | > .bp3-form-group { 61 | width: 100%; 62 | } 63 | ` 64 | 65 | export const HalfWrapper = styled(Wrapper)` 66 | width: 50%; 67 | ` 68 | -------------------------------------------------------------------------------- /views/redux/info/index.es: -------------------------------------------------------------------------------- 1 | import { combineReducers } from '../combine-reducers' 2 | import reduceReducers from 'reduce-reducers' 3 | import { get, pick } from 'lodash' 4 | 5 | import { reducer as basic } from './basic' 6 | import { reducer as ships } from './ships' 7 | import { reducer as fleets } from './fleets' 8 | import { reducer as equips } from './equips' 9 | import { reducer as repairs } from './repairs' 10 | import { reducer as constructions } from './constructions' 11 | import { reducer as resources } from './resources' 12 | import { reducer as maps } from './maps' 13 | import { reducer as quests } from './quests' 14 | import { reducer as server } from './server' 15 | import { reducer as useitems } from './useitems' 16 | import { reducer as airbase } from './airbase' 17 | import presets from './presets' 18 | 19 | export const reducer = reduceReducers( 20 | (state, action) => { 21 | if (action.type === '@@Response/kcsapi/api_get_member/require_info') { 22 | const oldAdmiralId = get(state, 'basic.api_member_id') 23 | const admiralId = action.body.api_basic.api_member_id 24 | if (oldAdmiralId != admiralId) { 25 | return pick(state, ['basic']) 26 | } 27 | } 28 | return state 29 | }, 30 | combineReducers({ 31 | basic, 32 | ships, 33 | fleets, 34 | equips, 35 | repairs, 36 | constructions, 37 | resources, 38 | maps, 39 | quests, 40 | airbase, 41 | presets, 42 | server, 43 | useitems, 44 | }), 45 | ) 46 | -------------------------------------------------------------------------------- /views/components/settings/about/contributors.es: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { map, memoize } from 'lodash' 3 | import CONTRIBUTORS from 'poi-asset-contributor-data/dist/contributors.json' 4 | import { shell } from 'electron' 5 | import { styled } from 'styled-components' 6 | 7 | import { Section } from 'views/components/settings/components/section' 8 | 9 | const getAvatarUrl = (url) => (/.*githubusercontent.com\/u\/.*/.test(url) ? `${url}&s=160` : url) 10 | 11 | const openLink = memoize((link) => () => shell.openExternal(link)) 12 | 13 | const Wrapper = styled.div` 14 | display: flex; 15 | flex-wrap: wrap; 16 | ` 17 | 18 | const Avatar = styled.div` 19 | margin: 5px; 20 | border-radius: 50%; 21 | height: 40px; 22 | width: 40px; 23 | transition: 0.3s; 24 | overflow: hidden; 25 | 26 | img { 27 | width: 100%; 28 | cursor: pointer; 29 | } 30 | 31 | :hover { 32 | filter: drop-shadow(0 0 4px rgb(255 255 255 / 0.25)); 33 | } 34 | ` 35 | 36 | export const Contributors = ({ ready }) => ( 37 |
38 | 39 | {ready && 40 | map(CONTRIBUTORS, (e, i) => ( 41 | 42 | 47 | 48 | ))} 49 | 50 |
51 | ) 52 | -------------------------------------------------------------------------------- /views/redux/plugins/index.es: -------------------------------------------------------------------------------- 1 | import { sortBy } from 'lodash' 2 | import { reduxSet } from 'views/utils/tools' 3 | 4 | export const sortPlugins = (ps) => sortBy(ps, ['priority', 'packageName']) 5 | 6 | export function reducer(state = [], { type, value, option }) { 7 | const findPluginIndexByPackageName = (packageName) => 8 | state.findIndex((p) => p.packageName === packageName) 9 | 10 | switch (type) { 11 | case '@@Plugin/initialize': { 12 | return value 13 | } 14 | case '@@Plugin/add': { 15 | const i = findPluginIndexByPackageName(value.packageName) 16 | if (i === -1) { 17 | state = state.concat(value) 18 | } else { 19 | state[i] = value 20 | } 21 | return sortPlugins(state) 22 | } 23 | case '@@Plugin/changeStatus': { 24 | const i = findPluginIndexByPackageName(value.packageName) 25 | if (!state[i]) { 26 | return state 27 | } 28 | let pluginToUpdate = { ...state[i] } 29 | for (const opt of option) { 30 | const { path, status } = opt 31 | pluginToUpdate = reduxSet(pluginToUpdate, path.split('.'), status) 32 | } 33 | state = [...state.slice(0, i), pluginToUpdate, ...state.slice(i + 1)] 34 | return sortPlugins(state) 35 | } 36 | case '@@Plugin/remove': { 37 | const i = findPluginIndexByPackageName(value.packageName) 38 | if (i !== -1) { 39 | state = [...state] 40 | state.splice(i, 1) 41 | } 42 | return state 43 | } 44 | } 45 | 46 | return state 47 | } 48 | -------------------------------------------------------------------------------- /views/components/settings/about/index.es: -------------------------------------------------------------------------------- 1 | import React, { useState, useRef, useEffect, useCallback } from 'react' 2 | import { styled } from 'styled-components' 3 | 4 | import { VersionInfo } from './version-info' 5 | import { AppMetrics } from './app-metrics' 6 | import { OpenCollective } from './open-collective' 7 | import { GPUStatus } from './gpu-status' 8 | import { Update } from './update' 9 | import { Contributors } from './contributors' 10 | import { ThanksTo } from './thanks-to' 11 | 12 | const TopSentinel = styled.div` 13 | position: relative; 14 | top: 4px; 15 | ` 16 | 17 | export const About = () => { 18 | const [ready, setReady] = useState(false) 19 | 20 | const sentinel = useRef(null) 21 | const observer = useRef(null) 22 | 23 | const handleIntersection = useCallback( 24 | ([entry]) => { 25 | if (entry.isIntersecting) { 26 | setReady(true) 27 | observer.current.disconnect() 28 | } 29 | }, 30 | [setReady, observer], 31 | ) 32 | 33 | useEffect(() => { 34 | observer.current = new IntersectionObserver(handleIntersection) 35 | observer.current.observe(sentinel.current) 36 | 37 | return () => observer.current.disconnect() 38 | }, [handleIntersection]) 39 | 40 | return ( 41 |
42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 |
51 | ) 52 | } 53 | -------------------------------------------------------------------------------- /views/components/settings/display/zooming-config.es: -------------------------------------------------------------------------------- 1 | /* global config */ 2 | import React, { Component } from 'react' 3 | import PropTypes from 'prop-types' 4 | import { connect } from 'react-redux' 5 | import { get } from 'lodash' 6 | import { Slider } from '@blueprintjs/core' 7 | import { withNamespaces } from 'react-i18next' 8 | 9 | import { Section } from 'views/components/settings/components/section' 10 | 11 | @withNamespaces(['setting']) 12 | @connect((state, props) => ({ 13 | zoomLevel: get(state.config, 'poi.appearance.zoom', 1), 14 | key: get(state.config, 'poi.appearance.zoom', 1), 15 | })) 16 | export class ZoomingConfig extends Component { 17 | static propTypes = { 18 | zoomLevel: PropTypes.number, 19 | } 20 | 21 | state = { 22 | zoomLevel: this.props.zoomLevel, 23 | } 24 | 25 | handleChangeZoomLevel = (value) => { 26 | this.setState({ 27 | zoomLevel: Math.round(value * 100) / 100, 28 | }) 29 | } 30 | 31 | handleSaveZoomLevel = (value) => { 32 | config.set('poi.appearance.zoom', this.state.zoomLevel) 33 | } 34 | 35 | render() { 36 | const { t } = this.props 37 | return ( 38 |
39 | `${Math.round(value * 100)}%`} 46 | value={this.state.zoomLevel} 47 | /> 48 |
49 | ) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/module-path.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/ban-ts-comment */ 2 | // @ts-ignore: module seems to be special 3 | import { Module } from 'module' 4 | import path from 'path' 5 | 6 | // @ts-ignore: patching internal variables 7 | const { _nodeModulePaths } = Module 8 | 9 | const allowedPaths = new Set() 10 | 11 | const MODULE_PATH = path.join(__dirname, '..', 'node_modules') 12 | 13 | /** 14 | * we search for a package in node modules, we will node_modules at every level 15 | * but some unexpected packages from upper node_modules would break our app 16 | * we use this to filter out upper folders 17 | * @param modulePaths {string[]} the limit we want 18 | */ 19 | export const setAllowedPath = function (...modulePaths: string[]) { 20 | modulePaths.forEach(function (modulePath) { 21 | allowedPaths.add(modulePath) 22 | }) 23 | const allowedPathsArray = Array.from(allowedPaths) 24 | // @ts-ignore: patching internal variables 25 | Module._nodeModulePaths = function (from: string) { 26 | // use function style instead of arrows, expecting some possible perf gain 27 | 28 | const originResult = _nodeModulePaths.call(this, from) 29 | 30 | // if require path is including module path, do not put allowed path so that 31 | // require priority won't be messed up 32 | const ignoreAllowedPath = from.startsWith(MODULE_PATH) 33 | 34 | // put allowed path in front so that main program's module path has higher priority 35 | return ignoreAllowedPath ? originResult : allowedPathsArray.concat(originResult) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /views/components/settings/components/checkbox.es: -------------------------------------------------------------------------------- 1 | /* global config */ 2 | import React, { Component } from 'react' 3 | import PropTypes from 'prop-types' 4 | import { connect } from 'react-redux' 5 | import { get } from 'lodash' 6 | import { Checkbox } from '@blueprintjs/core' 7 | 8 | @connect((state, props) => ({ 9 | value: get(state.config, props.configName, props.defaultValue), 10 | configName: props.configName, 11 | undecided: props.undecided, 12 | label: props.label, 13 | })) 14 | export class CheckboxLabelConfig extends Component { 15 | static propTypes = { 16 | label: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), 17 | configName: PropTypes.string, 18 | value: PropTypes.bool, 19 | undecided: PropTypes.bool, 20 | defaultValue: PropTypes.bool, 21 | } 22 | 23 | componentDidMount = () => { 24 | if (typeof this.props.defaultVal !== 'undefined') { 25 | console.error('prop `defaultVal` is deprecated, use `defaultValue` instaed') 26 | } 27 | } 28 | 29 | handleChange = () => { 30 | config.set(this.props.configName, !this.props.value) 31 | } 32 | 33 | render() { 34 | return ( 35 |
36 | 41 | {this.props.label} 42 | 43 |
44 | ) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /views/components/settings/main/language-config.es: -------------------------------------------------------------------------------- 1 | /* global config */ 2 | import React, { Component } from 'react' 3 | import PropTypes from 'prop-types' 4 | import { connect } from 'react-redux' 5 | import { get, each, map } from 'lodash' 6 | import i18next from 'views/env-parts/i18next' 7 | import { HTMLSelect } from '@blueprintjs/core' 8 | import { withNamespaces } from 'react-i18next' 9 | 10 | import { Section } from '../components/section' 11 | 12 | const setWindowI18nLng = (language) => { 13 | each(i18next.options.ns, (ns) => { 14 | window.i18n[ns].fixedT = i18next.getFixedT(language, ns) 15 | }) 16 | } 17 | 18 | @withNamespaces(['setting']) 19 | @connect((state, props) => ({ 20 | value: get(state.config, 'poi.misc.language', window.language), 21 | })) 22 | export class LanguageConfig extends Component { 23 | static propTypes = { 24 | value: PropTypes.string, 25 | } 26 | 27 | handleSetLanguage = (e) => { 28 | const language = e.currentTarget.value 29 | config.set('poi.misc.language', language) 30 | i18next.changeLanguage(language) 31 | setWindowI18nLng(language) 32 | } 33 | 34 | render() { 35 | const { t } = this.props 36 | return ( 37 |
38 | 39 | {map(window.LOCALES, (lng) => ( 40 | 43 | ))} 44 | 45 |
46 | ) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /views/redux/info/constructions.ts: -------------------------------------------------------------------------------- 1 | import { APIKdock } from 'kcsapi/api_get_member/require_info/response' 2 | import { 3 | createAPIGetMemberKdockResponseAction, 4 | createAPIGetMemberRequireInfoAction, 5 | createAPIReqKousyouCreateShipSpeedChangeResponseAction, 6 | createAPIReqKousyouGetShipResponseAction, 7 | } from '../actions' 8 | import { createSlice } from '@reduxjs/toolkit' 9 | 10 | const completeConstruction = { 11 | api_complete_time: 0, 12 | api_complete_time_str: '0', 13 | api_state: 3, 14 | } 15 | 16 | const constructionsSlice = createSlice({ 17 | name: 'constructions', 18 | initialState: [] as APIKdock[], 19 | reducers: {}, 20 | extraReducers: (builder) => { 21 | builder 22 | .addCase(createAPIGetMemberRequireInfoAction, (state, { payload }) => { 23 | return payload.body.api_kdock 24 | }) 25 | .addCase(createAPIReqKousyouGetShipResponseAction, (state, { payload }) => { 26 | return payload.body.api_kdock 27 | }) 28 | .addCase(createAPIGetMemberKdockResponseAction, (state, { payload }) => { 29 | return payload.body 30 | }) 31 | .addCase(createAPIReqKousyouCreateShipSpeedChangeResponseAction, (state, { payload }) => { 32 | const { api_kdock_id } = payload.postBody 33 | const dockId = parseInt(api_kdock_id, 10) 34 | const newState = state.slice() 35 | newState[dockId - 1] = Object.assign({}, newState[dockId - 1], completeConstruction) 36 | return newState 37 | }) 38 | }, 39 | }) 40 | 41 | export const reducer = constructionsSlice.reducer 42 | -------------------------------------------------------------------------------- /views/services/sortie-free-slot-check.es: -------------------------------------------------------------------------------- 1 | /* global error, config, getStore */ 2 | import i18next from 'views/env-parts/i18next' 3 | import { getSlotitemCount } from 'views/utils/game-utils' 4 | 5 | window.addEventListener('game.response', ({ detail: { path, body } }) => { 6 | if (path === '/kcsapi/api_get_member/mapinfo') { 7 | const basic = getStore('info.basic') 8 | const errMsg = [] 9 | if (config.get('poi.mapStartCheck.ship.enable', false)) { 10 | const minShipSlots = config.get('poi.mapStartCheck.ship.minFreeSlots', 4) 11 | const shipSlots = basic.api_max_chara - Object.keys(getStore('info.ships')).length 12 | if (shipSlots < minShipSlots) { 13 | if (shipSlots > 0) { 14 | errMsg.push(i18next.t('main:ShipSlotWarning', { count: shipSlots })) 15 | } else { 16 | errMsg.push(i18next.t('main:ShipSlotFull')) 17 | } 18 | } 19 | } 20 | if (config.get('poi.mapStartCheck.item.enable', false)) { 21 | const minEquipSlots = config.get('poi.mapStartCheck.item.minFreeSlots', 10) 22 | const equipSlots = basic.api_max_slotitem - getSlotitemCount(getStore('info.equips')) 23 | if (equipSlots < minEquipSlots) { 24 | if (equipSlots > 0) { 25 | errMsg.push(i18next.t('main:EquipSlotWarning', { count: equipSlots })) 26 | } else { 27 | errMsg.push(i18next.t('main:EquipSlotFull')) 28 | } 29 | } 30 | } 31 | if (errMsg.length > 0) { 32 | const msg = errMsg.join('') 33 | setTimeout(() => error(msg), 1000) 34 | } 35 | } 36 | }) 37 | -------------------------------------------------------------------------------- /views/components/settings/gaming/pre-sortie-config/unused-slot-check-config.es: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import PropTypes from 'prop-types' 3 | import { connect } from 'react-redux' 4 | import { get } from 'lodash' 5 | import { withNamespaces } from 'react-i18next' 6 | import { FormGroup } from '@blueprintjs/core' 7 | 8 | import { SwitchConfig } from 'views/components/settings/components/switch' 9 | import { Wrapper } from 'views/components/settings/components/section' 10 | 11 | @withNamespaces(['setting']) 12 | @connect((state, props) => ({ 13 | enable: get(state.config, `poi.unusedEquipmentSlotCheck.enable`, false), 14 | ignoreUnlocked: get(state.config, `poi.unusedEquipmentSlotCheck.ignoreUnlocked`, false), 15 | })) 16 | export class UnusedSlotCheckConfig extends Component { 17 | static propTypes = { 18 | enable: PropTypes.bool, 19 | ignoreUnlocked: PropTypes.bool, 20 | } 21 | 22 | render() { 23 | const { t, enable, ignoreUnlocked } = this.props 24 | return ( 25 | 26 | 27 | 32 | 38 | 39 | 40 | ) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /views/components/settings/about/thanks-to.es: -------------------------------------------------------------------------------- 1 | /* global CONST */ 2 | import React from 'react' 3 | import { map, memoize } from 'lodash' 4 | import { shell } from 'electron' 5 | import { styled } from 'styled-components' 6 | 7 | import { Section } from 'views/components/settings/components/section' 8 | 9 | const openLink = memoize((link) => () => shell.openExternal(link)) 10 | 11 | const Wrapper = styled.div` 12 | display: flex; 13 | flex-wrap: wrap; 14 | align-items: center; 15 | ` 16 | 17 | const Item = styled.div` 18 | width: 50%; 19 | padding-left: 15px; 20 | padding-right: 15px; 21 | display: flex; 22 | align-items: center; 23 | ` 24 | 25 | const Icon = styled.div` 26 | border-radius: 4px; 27 | height: 75px; 28 | width: 75px; 29 | overflow: hidden; 30 | 31 | img { 32 | cursor: pointer; 33 | } 34 | ` 35 | 36 | const Detail = styled.div` 37 | flex: 1; 38 | padding-left: 5px; 39 | ` 40 | 41 | export const ThanksTo = ({ ready }) => ( 42 |
43 | 44 | {map(CONST.thanksTo, (e) => ( 45 | 46 | 47 | {ready && ( 48 | 49 | )} 50 | 51 | 52 | {e.name} 53 |

{e.description}

54 |
55 |
56 | ))} 57 |
58 |
59 | ) 60 | -------------------------------------------------------------------------------- /views/components/settings/components/text.es: -------------------------------------------------------------------------------- 1 | /* global config */ 2 | import React, { PureComponent } from 'react' 3 | import PropTypes from 'prop-types' 4 | import { connect } from 'react-redux' 5 | import { get, debounce } from 'lodash' 6 | import { InputGroup } from '@blueprintjs/core' 7 | 8 | @connect((state, props) => ({ 9 | value: get(state.config, props.configName, props.defaultValue || ''), 10 | configName: props.configName, 11 | label: props.label, 12 | })) 13 | export class TextConfig extends PureComponent { 14 | static propTypes = { 15 | configName: PropTypes.string.isRequired, 16 | value: PropTypes.string.isRequired, 17 | disabled: PropTypes.bool, 18 | defaultValue: PropTypes.string, 19 | } 20 | 21 | static getDerivedStateFromProps = (nextProps, prevState) => { 22 | if (nextProps.value !== prevState.propValue) { 23 | return { 24 | value: nextProps.value, 25 | propValue: nextProps.value, 26 | } 27 | } else { 28 | return null 29 | } 30 | } 31 | 32 | state = { 33 | value: this.props.value, 34 | propValue: this.props.value, 35 | } 36 | 37 | handleChange = (e) => { 38 | this.setState({ 39 | value: e.currentTarget.value, 40 | }) 41 | this.applyConfig(e.currentTarget.value) 42 | } 43 | 44 | applyConfig = debounce((value) => { 45 | config.set(this.props.configName, value) 46 | }, 200) 47 | 48 | render() { 49 | const { configName, defaultValue, dispatch, ...rest } = this.props 50 | return 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /assets/js/page-align.js: -------------------------------------------------------------------------------- 1 | // Faster align setting 2 | const alignCSS = document.createElement('style') 3 | alignCSS.innerHTML = `html { 4 | overflow: hidden; 5 | } 6 | #w, #main-ntg { 7 | position: absolute !important; 8 | top: 0; 9 | left: 0; 10 | z-index: 100; 11 | margin-left: 0 !important; 12 | margin-top: 0 !important; 13 | } 14 | #game_frame { 15 | width: 1200px !important; 16 | position: absolute; 17 | top: 0px; 18 | left: 0; 19 | } 20 | .naviapp { 21 | z-index: -1; 22 | } 23 | #ntg-recommend { 24 | display: none !important; 25 | } 26 | #spacing_top { 27 | display: none; 28 | } 29 | #alert { 30 | left: 270px !important; 31 | top: 83px !important; 32 | border: 0; 33 | } 34 | aside { 35 | display: none !important; 36 | } 37 | ` 38 | 39 | window.align = function () { 40 | if ( 41 | location.href === 'https://games.dmm.com/detail/kancolle' || 42 | location.hostname === 'accounts.dmm.com' 43 | ) { 44 | return 45 | } 46 | if ( 47 | location.pathname.includes('kancolle') || 48 | location.pathname.includes('854854') || 49 | location.hostname === 'osapi.dmm.com' || 50 | location.pathname.includes('kcs') 51 | ) { 52 | document.body.appendChild(alignCSS) 53 | window.scrollTo(0, 0) 54 | } 55 | } 56 | 57 | window.unalign = () => { 58 | if (document.body.contains(alignCSS)) { 59 | document.body.removeChild(alignCSS) 60 | } 61 | } 62 | 63 | const handleDocumentReady = () => { 64 | if (!document.body) { 65 | setTimeout(handleDocumentReady, 1000) 66 | return 67 | } 68 | window.align() 69 | } 70 | 71 | handleDocumentReady() 72 | -------------------------------------------------------------------------------- /views/components/settings/main/accessibility-config.es: -------------------------------------------------------------------------------- 1 | /* global config */ 2 | import React, { Component } from 'react' 3 | import PropTypes from 'prop-types' 4 | import { connect } from 'react-redux' 5 | import { get, map } from 'lodash' 6 | import { HTMLSelect } from '@blueprintjs/core' 7 | import { withNamespaces } from 'react-i18next' 8 | import { styled } from 'styled-components' 9 | 10 | import { Section } from '../components/section' 11 | 12 | const list = [ 13 | 'none', 14 | 'protanopia', 15 | 'protanomaly', 16 | 'deuteranopia', 17 | 'deuteranomaly', 18 | 'tritanopia', 19 | 'tritanomaly', 20 | 'achromatopsia', 21 | 'achromatomaly', 22 | ] 23 | 24 | const Select = styled(HTMLSelect)` 25 | margin-left: 8px; 26 | ` 27 | 28 | @withNamespaces(['setting']) 29 | @connect((state, props) => ({ 30 | value: get(state.config, 'poi.appearance.colorblindFilter', 'null'), 31 | })) 32 | export class AccessibilityConfig extends Component { 33 | static propTypes = { 34 | value: PropTypes.string, 35 | } 36 | 37 | handleSetFilter = (e) => { 38 | config.set('poi.appearance.colorblindFilter', e.currentTarget.value) 39 | } 40 | 41 | render() { 42 | const { t } = this.props 43 | return ( 44 |
45 | {t('setting:Color blind mode')} 46 | 53 |
54 | ) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/__tests__/config.spec.es: -------------------------------------------------------------------------------- 1 | jest.mock('fs-extra', () => ({ 2 | accessSync: jest.fn(), 3 | writeFileSync: jest.fn(), 4 | constants: {}, 5 | })) 6 | jest.mock('cson') 7 | jest.mock('../default-config.ts', () => ({ foo: 'bar' })) 8 | import config from '../config' 9 | import fs from 'fs-extra' 10 | import CSON from 'cson' 11 | 12 | describe('config', () => { 13 | it('initially, should be filled with default value', () => { 14 | expect(CSON.parseCSONFile.mock.calls).toMatchSnapshot() 15 | expect(config.get('', null)).toMatchSnapshot() 16 | }) 17 | 18 | it('should be set and get a string correctly', () => { 19 | config.set('path.to.value', 'Dead Beef') 20 | expect(config.get('path.to.value', null)).toBe('Dead Beef') 21 | expect(fs.writeFileSync).toHaveBeenCalled() 22 | expect(CSON.stringify.mock.calls).toMatchSnapshot() 23 | expect(fs.writeFileSync.mock.calls[0][0].includes(global.EXROOT)).toBe(true) 24 | expect(fs.writeFileSync.mock.calls[0][1]).toMatchSnapshot() 25 | }) 26 | it('should be set and get a object correctly', () => { 27 | config.set('another', { 28 | a: 'b', 29 | c: 0xbb, 30 | }) 31 | expect(fs.writeFileSync).toHaveBeenCalled() 32 | expect(CSON.stringify.mock.calls).toMatchSnapshot() 33 | expect(fs.writeFileSync.mock.calls[0][0].includes(global.EXROOT)).toBe(true) 34 | expect(fs.writeFileSync.mock.calls[0][1]).toMatchSnapshot() 35 | expect(config.get('another', null)).toEqual({ 36 | a: 'b', 37 | c: 0xbb, 38 | }) 39 | }) 40 | it('should be get a null', () => { 41 | expect(config.get('null.path', null)).toBe(null) 42 | }) 43 | }) 44 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | poi 7 | 8 | 9 | 10 | 11 | 12 | 17 | 18 | 19 |
20 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /views/components/ship/slotitems-data.es: -------------------------------------------------------------------------------- 1 | import i18next from 'views/env-parts/i18next' 2 | 3 | const types = { 4 | api_taik: 'HP', 5 | api_souk: 'Armor', 6 | api_houg: 'Firepower', 7 | api_raig: 'Torpedo', 8 | api_soku: 'Speed', 9 | api_baku: 'Bombing', 10 | api_tyku: 'AA', 11 | api_tais: 'ASW', 12 | api_houm: 'Accuracy', 13 | //"api_raim": "Torpedo Accuracy", 14 | api_houk: 'Evasion', 15 | //"api_raik": "Torpedo Evasion", 16 | //"api_bakk": "Bombing Evasion", 17 | api_saku: 'LOS', 18 | //"api_sakb": "Anti-LOS", 19 | api_luck: 'Luck', 20 | api_leng: 'Range', 21 | } 22 | 23 | const landbaseFighterTypes = { 24 | api_houm: 'Anti-Bomber', 25 | api_houk: 'Interception', 26 | } 27 | 28 | const range = ['Short', 'Medium', 'Long', 'Very Long'] 29 | 30 | export function getItemData(slotitem) { 31 | const data = [] 32 | for (const type in types) { 33 | if (slotitem[type] && slotitem[type] != 0) { 34 | if (type == 'api_leng') { 35 | data.push( 36 | `${i18next.t('data:' + types[type])} ${i18next.t('data:' + range[slotitem[type] - 1])}`, 37 | ) 38 | } else if ( 39 | [48].includes(slotitem.api_type[2]) && 40 | ['api_houk', 'api_houm'].includes(type) && 41 | slotitem[type] > 0 42 | ) { 43 | data.push(`${i18next.t('data:' + landbaseFighterTypes[type])} +${slotitem[type]}`) 44 | } else if (slotitem[type] > 0) { 45 | data.push(`${i18next.t('data:' + types[type])} +${slotitem[type]}`) 46 | } else { 47 | data.push(`${i18next.t('data:' + types[type])} ${slotitem[type]}`) 48 | } 49 | } 50 | } 51 | return data 52 | } 53 | -------------------------------------------------------------------------------- /views/components/settings/main/advanced-config/limit-fps.es: -------------------------------------------------------------------------------- 1 | /* global config */ 2 | import React, { useState } from 'react' 3 | import { withNamespaces } from 'react-i18next' 4 | import { connect } from 'react-redux' 5 | import { get } from 'lodash' 6 | import { compose } from 'redux' 7 | 8 | import { Slider, FormGroup } from '@blueprintjs/core' 9 | 10 | import { Wrapper, HalfWrapper } from 'views/components/settings/components/section' 11 | import { SwitchConfig } from 'views/components/settings/components/switch' 12 | 13 | const handleChangeLimit = (value) => { 14 | config.set('poi.misc.limitFps.value', parseInt(value)) 15 | } 16 | 17 | export const LimitFps = compose( 18 | withNamespaces(['setting']), 19 | connect((state) => get(state.config, 'poi.misc.limitFps')), 20 | )(({ t, enabled, value }) => { 21 | const [fps, setFps] = useState(value) 22 | 23 | return ( 24 | 25 | 26 | 27 | 32 | 33 | 34 | 35 | 36 | 37 | setFps(v)} 40 | onRelease={handleChangeLimit} 41 | min={30} 42 | max={120} 43 | stepSize={5} 44 | value={fps} 45 | labelStepSize={30} 46 | /> 47 | 48 | 49 | 50 | ) 51 | }) 52 | -------------------------------------------------------------------------------- /views/components/settings/about/gpu-status.es: -------------------------------------------------------------------------------- 1 | /* global ROOT */ 2 | import React, { PureComponent } from 'react' 3 | import * as remote from '@electron/remote' 4 | import path from 'path-extra' 5 | import { Button, Intent } from '@blueprintjs/core' 6 | import { withNamespaces } from 'react-i18next' 7 | 8 | import { Section } from 'views/components/settings/components/section' 9 | import { fileUrl } from 'views/utils/tools' 10 | 11 | @withNamespaces(['setting']) 12 | export class GPUStatus extends PureComponent { 13 | getGPUFeatureStatus = remote.require('electron').app.getGPUFeatureStatus 14 | 15 | handleClick = () => { 16 | //As of electron 11, the url 'chrome://gpu' is not working. 17 | const gpuWindow = open(fileUrl(path.join(ROOT, 'index-plugin.html')), 'plugin[gpuinfo]') 18 | gpuWindow.addEventListener('DOMContentLoaded', () => { 19 | const div = gpuWindow.document.createElement('div') 20 | const color = window.isDarkTheme ? '#3d3d3d' : 'white' 21 | div.style.height = '100%' 22 | div.innerHTML = 23 | '' 24 | gpuWindow.document.body.style.height = '100vh' 25 | gpuWindow.document.body.style.margin = 0 26 | gpuWindow.document.body.style.backgroundColor = color 27 | gpuWindow.document.body.appendChild(div) 28 | }) 29 | } 30 | 31 | render() { 32 | const { t } = this.props 33 | return ( 34 |
35 | 38 |
39 | ) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /views/redux/info/__tests__/quests.spec.es: -------------------------------------------------------------------------------- 1 | import { getTanakalendarQuarterMonth } from '../quests' 2 | import moment from 'moment-timezone' 3 | import { padStart } from 'lodash' 4 | import Scheduler from 'views/services/scheduler' 5 | jest.mock('@electron/remote', () => ({ require })) 6 | 7 | const spec = it 8 | 9 | /** 10 | * creates a date for first quest refresh of given year and month 11 | * @param {number} year 12 | * @param {number} month 13 | */ 14 | const createDate = (year, month) => 15 | new Date(+moment.tz(`${year}-${padStart(String(month), 2, '0')}-01 05:00`, 'Asia/Tokyo')) 16 | 17 | const testCase = (year, month, expected) => 18 | expect(getTanakalendarQuarterMonth(createDate(year, month))).toStrictEqual(expected) 19 | 20 | describe('getTanakalendarQuarterMonth', () => { 21 | spec('sample of a full year', () => { 22 | const qmBase = getTanakalendarQuarterMonth(createDate(2019, 1)) 23 | // this "quarter" is relative, we don't care about its actual value 24 | // but will expect the following months to be consistent. 25 | const q0 = qmBase[0] 26 | // expect the first month of a year to be the second month in that quarter. 27 | expect(qmBase[1]).toBe(1) 28 | 29 | testCase(2019, 2, [q0, 2]) 30 | 31 | testCase(2019, 3, [q0 + 1, 0]) 32 | testCase(2019, 4, [q0 + 1, 1]) 33 | testCase(2019, 5, [q0 + 1, 2]) 34 | 35 | testCase(2019, 6, [q0 + 2, 0]) 36 | testCase(2019, 7, [q0 + 2, 1]) 37 | testCase(2019, 8, [q0 + 2, 2]) 38 | 39 | testCase(2019, 9, [q0 + 3, 0]) 40 | testCase(2019, 10, [q0 + 3, 1]) 41 | testCase(2019, 11, [q0 + 3, 2]) 42 | 43 | testCase(2019, 12, [q0 + 4, 0]) 44 | }) 45 | 46 | Scheduler._stopTick() 47 | }) 48 | -------------------------------------------------------------------------------- /assets/data/server.json: -------------------------------------------------------------------------------- 1 | { 2 | "w01y.kancolle-server.com": { 3 | "num": 1, 4 | "name": "横須賀鎮守府" 5 | }, 6 | "w02k.kancolle-server.com": { 7 | "num": 2, 8 | "name": "呉鎮守府" 9 | }, 10 | "w03s.kancolle-server.com": { 11 | "num": 3, 12 | "name": "佐世保鎮守府" 13 | }, 14 | "w04m.kancolle-server.com": { 15 | "num": 4, 16 | "name": "舞鶴鎮守府" 17 | }, 18 | "w05o.kancolle-server.com": { 19 | "num": 5, 20 | "name": "大湊警備府" 21 | }, 22 | "w06t.kancolle-server.com": { 23 | "num": 6, 24 | "name": "トラック泊地" 25 | }, 26 | "w07l.kancolle-server.com": { 27 | "num": 7, 28 | "name": "リンガ泊地" 29 | }, 30 | "w08r.kancolle-server.com": { 31 | "num": 8, 32 | "name": "ラバウル基地" 33 | }, 34 | "w09s.kancolle-server.com": { 35 | "num": 9, 36 | "name": "ショートランド泊地" 37 | }, 38 | "w10b.kancolle-server.com": { 39 | "num": 10, 40 | "name": "ブイン基地" 41 | }, 42 | "w11t.kancolle-server.com": { 43 | "num": 11, 44 | "name": "タウイタウイ泊地" 45 | }, 46 | "w12p.kancolle-server.com": { 47 | "num": 12, 48 | "name": "パラオ泊地" 49 | }, 50 | "w13b.kancolle-server.com": { 51 | "num": 13, 52 | "name": "ブルネイ泊地" 53 | }, 54 | "w14h.kancolle-server.com": { 55 | "num": 14, 56 | "name": "単冠湾泊地" 57 | }, 58 | "w15p.kancolle-server.com": { 59 | "num": 15, 60 | "name": "幌筵泊地" 61 | }, 62 | "w16s.kancolle-server.com": { 63 | "num": 16, 64 | "name": "宿毛湾泊地" 65 | }, 66 | "w17k.kancolle-server.com": { 67 | "num": 17, 68 | "name": "鹿屋基地" 69 | }, 70 | "w18i.kancolle-server.com": { 71 | "num": 18, 72 | "name": "岩川基地" 73 | }, 74 | "w19s.kancolle-server.com": { 75 | "num": 19, 76 | "name": "佐伯湾泊地" 77 | }, 78 | "w20h.kancolle-server.com": { 79 | "num": 20, 80 | "name": "柱島泊地" 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | require('@babel/register')(require('./babel-register.config')) 2 | const gulp = require('gulp') 3 | const childProcess = require('child_process') 4 | const { trim } = require('lodash') 5 | 6 | global.ROOT = __dirname 7 | 8 | const { log } = require('./build/utils') 9 | const { build, installPlugins, cleanFiles, packWinRelease, deployNightlies } = require('./build') 10 | 11 | const packageMeta = require('./package.json') 12 | 13 | let poiVersion = null 14 | 15 | gulp.task('getVersion', (done) => { 16 | const package_version = packageMeta.version 17 | poiVersion = package_version 18 | childProcess.exec('git rev-parse HEAD', (err, stdout) => { 19 | if (!err) { 20 | global.latestCommit = trim(stdout) 21 | } else { 22 | console.error(err) 23 | } 24 | log(`*** Start building poi ${poiVersion} at ${global.latestCommit} ***`) 25 | done() 26 | }) 27 | }) 28 | 29 | gulp.task( 30 | 'build', 31 | gulp.series('getVersion', () => build(poiVersion)), 32 | ) 33 | 34 | gulp.task( 35 | 'build_plugins', 36 | gulp.series('getVersion', () => installPlugins(poiVersion)), 37 | ) 38 | 39 | gulp.task( 40 | 'pack_win_release', 41 | gulp.series('getVersion', () => packWinRelease(poiVersion)), 42 | ) 43 | 44 | gulp.task('deploy_nightlies', () => deployNightlies()) 45 | 46 | gulp.task('clean', () => cleanFiles()) 47 | 48 | gulp.task('default', (done) => { 49 | log` 50 | Usage: 51 | gulp deploy - Make this repo ready to use 52 | gulp build - Build release complete packages under ./dist/ 53 | gulp build_plugins - Pack up latest plugin tarballs under ./dist/ 54 | 55 | extra arguments will be passed to npm if it is used 56 | ` 57 | done() 58 | }) 59 | -------------------------------------------------------------------------------- /views/components/ship/aapb-indicator.es: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { connect } from 'react-redux' 3 | import { createSelector } from 'reselect' 4 | import { memoize, compact, isFinite } from 'lodash' 5 | import { withNamespaces } from 'react-i18next' 6 | import { Tooltip, Tag, Position, Intent } from '@blueprintjs/core' 7 | import { compose } from 'redux' 8 | 9 | import { shipDataSelectorFactory, shipEquipDataSelectorFactory } from 'views/utils/selectors' 10 | import { getShipAAPB } from 'views/utils/aapb' 11 | import { ShipLabel } from 'views/components/ship-parts/styled-components' 12 | 13 | const AAPBSelectorFactory = memoize((shipId) => 14 | createSelector( 15 | [shipDataSelectorFactory(shipId), shipEquipDataSelectorFactory(shipId)], 16 | (shipInfo, equipsInfo) => { 17 | if (!shipInfo || !equipsInfo) return 0 18 | /* 19 | equipment position is irrelevant with regard to AAPB trigger rate, 20 | so we might as well remove all `undefined` for getShipAAPB to 21 | have a uniform structure to work with. 22 | */ 23 | return getShipAAPB(shipInfo, compact(equipsInfo)) 24 | }, 25 | ), 26 | ) 27 | 28 | export const AAPBIndicator = compose( 29 | withNamespaces(['main']), 30 | connect((state, { shipId }) => ({ 31 | AAPB: AAPBSelectorFactory(shipId)(state) || 0, 32 | })), 33 | )( 34 | ({ AAPB, shipId, t }) => 35 | isFinite(AAPB) && 36 | AAPB > 0 && ( 37 | 38 | {`${AAPB.toFixed(2)}%`}}> 39 | 40 | {t('main:AAPB')} 41 | 42 | 43 | 44 | ), 45 | ) 46 | -------------------------------------------------------------------------------- /views/components/settings/about/open-collective.es: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { range, debounce } from 'lodash' 3 | import { ResizeSensor } from '@blueprintjs/core' 4 | import { styled } from 'styled-components' 5 | 6 | import { Section } from 'views/components/settings/components/section' 7 | 8 | const floor = (x) => Math.floor(x / 10) * 10 9 | 10 | const Wrapper = styled.div` 11 | img { 12 | cursor: pointer; 13 | } 14 | ` 15 | 16 | export class OpenCollective extends Component { 17 | state = { 18 | width: 0, 19 | } 20 | 21 | handleResize = debounce(([entry]) => { 22 | this.setState({ 23 | width: floor(entry.contentRect.width), 24 | }) 25 | }, 100) 26 | 27 | render() { 28 | const { width } = this.state 29 | const { ready } = this.props 30 | return ( 31 | 32 |
33 | 34 | {ready && ( 35 |
36 | {range(10).map((i) => ( 37 | 38 | 39 | 40 | ))} 41 |
42 | )} 43 | {ready && width > 0 && ( 44 |
45 | 46 | 47 | 48 |
49 | )} 50 |
51 |
52 |
53 | ) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /views/components/settings/plugin/plugin-setting-wrapper.es: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import PropTypes from 'prop-types' 3 | import { withNamespaces } from 'react-i18next' 4 | import { TextArea, Button, Intent } from '@blueprintjs/core' 5 | import * as Sentry from '@sentry/electron' 6 | 7 | @withNamespaces(['setting']) 8 | export class PluginSettingWrapper extends Component { 9 | static propTypes = { 10 | plugin: PropTypes.object, 11 | } 12 | 13 | state = { 14 | hasError: false, 15 | error: null, 16 | info: null, 17 | } 18 | 19 | componentDidCatch = (error, info) => { 20 | Sentry.withScope((scope) => { 21 | scope.setExtra('componentStack', info.componentStack) 22 | scope.setTag('area', this.props.plugin.id) 23 | const eventId = Sentry.captureException(error) 24 | this.setState({ 25 | hasError: true, 26 | error, 27 | info, 28 | eventId, 29 | }) 30 | }) 31 | } 32 | 33 | shouldComponentUpdate = (nextProps, nextState) => 34 | this.props.plugin.timestamp !== nextProps.plugin.timestamp || nextState.hasError === true 35 | 36 | render() { 37 | const { hasError, error, info } = this.state 38 | const { plugin, t } = this.props 39 | if (hasError) { 40 | const code = [error.stack, info.componentStack].join('\n') 41 | return ( 42 |
43 |

{t('PluginErrTitle', { name: plugin.name })}

44 |

{t('PluginErrorMsg')}

45 |