diff --git a/fixChrome.cjs b/fixChrome.cjs
deleted file mode 100644
index e68be20..0000000
--- a/fixChrome.cjs
+++ /dev/null
@@ -1,11 +0,0 @@
-console.log('fixChrome.js loaded');
-
-const fs = require('fs')
-
-const manifest = require('./dist/manifest.json')
-manifest.web_accessible_resources[0].resources.push('index.html')
-manifest.action.default_popup = 'popup.html'
-//写回文件
-fs.writeFileSync('./dist/manifest.json', JSON.stringify(manifest, null, 2))
-
-console.log('fixChrome.js done');
diff --git a/fixFirefox.cjs b/fixFirefox.cjs
deleted file mode 100644
index af9b823..0000000
--- a/fixFirefox.cjs
+++ /dev/null
@@ -1,22 +0,0 @@
-console.log('fixFirefox.js loaded');
-
-const fs = require('fs')
-
-const manifest = require('./dist/manifest.json')
-manifest.web_accessible_resources[0].resources.push('index.html')
-manifest.action.default_popup = 'popup.html'
-//browser_specific_settings
-manifest.browser_specific_settings = {
- "gecko": {
- "id": "bilibili-subtitle@indiekky"
- }
-}
-//background
-manifest.background = {
- type: "module",
- scripts: [manifest.background.service_worker]
-}
-//写回文件
-fs.writeFileSync('./dist/manifest.json', JSON.stringify(manifest, null, 2))
-
-console.log('fixFirefox.js done');
diff --git a/manifest.config.ts b/manifest.config.ts
new file mode 100644
index 0000000..55c8394
--- /dev/null
+++ b/manifest.config.ts
@@ -0,0 +1,63 @@
+import {defineManifest} from '@crxjs/vite-plugin'
+// @ts-ignore
+import packageJson from './package.json'
+
+const {version} = packageJson
+
+// Convert from Semver (example: 0.1.0-beta6)
+const [major, minor, patch, label = '0'] = version
+ // can only contain digits, dots, or dash
+ .replace(/[^\d.-]+/g, '')
+ // split into version parts
+ .split(/[.-]/)
+
+export default defineManifest(async (env) => ({
+ "name": "哔哩哔哩字幕列表",
+ "description": "显示B站视频的字幕列表,可点击跳转与下载字幕,并支持翻译和总结字幕!",
+ "version": `${major}.${minor}.${patch}`,
+ "manifest_version": 3,
+ "permissions": [
+ "storage",
+ ],
+ "host_permissions": [
+ "http://localhost/*",
+ "http://127.0.0.1/*"
+ ],
+ "background": {
+ "service_worker": "src/chrome/background.ts",
+ "type": "module"
+ },
+ "options_page": "options.html",
+ "content_scripts": [
+ {
+ "matches": ["https://www.bilibili.com/video/*", "https://www.bilibili.com/list/*"],
+ "js": ["src/inject/inject.ts"]
+ }
+ ],
+ "icons": {
+ "16": "favicon-16x16.png",
+ "32": "favicon-32x32.png",
+ "48": "favicon-48x48.png",
+ "128": "favicon-128x128.png"
+ },
+ "action": {
+ "default_popup": "popup.html",
+ "default_icon": {
+ "16": "favicon-16x16.png",
+ "32": "favicon-32x32.png",
+ "48": "favicon-48x48.png",
+ "128": "favicon-128x128.png"
+ }
+ },
+ "web_accessible_resources": [
+ {
+ "matches": [
+ "https://www.bilibili.com/video/*", "https://www.bilibili.com/list/*"
+ ],
+ "resources": [
+ "index.html",
+ ],
+ "use_dynamic_url": true
+ }
+ ]
+}))
diff --git a/manifest.json b/manifest.json
deleted file mode 100644
index f2f73f3..0000000
--- a/manifest.json
+++ /dev/null
@@ -1,37 +0,0 @@
-{
- "name": "哔哩哔哩字幕列表",
- "description": "显示B站视频的字幕列表,可点击跳转与下载字幕,并支持翻译和总结字幕!",
- "version": "1.10.6",
- "manifest_version": 3,
- "permissions": [
- "storage"
- ],
- "host_permissions": [
- "http://localhost/*",
- "http://127.0.0.1/*"
- ],
- "background": {
- "service_worker": "src/chrome/background.ts"
- },
- "content_scripts": [
- {
- "matches": ["https://www.bilibili.com/video/*", "https://www.bilibili.com/list/*"],
- "js": ["src/chrome/content-script.cjs"]
- }
- ],
- "icons": {
- "16": "favicon-16x16.png",
- "32": "favicon-32x32.png",
- "48": "favicon-48x48.png",
- "128": "favicon-128x128.png"
- },
- "action": {
- "default_popup": "index.html",
- "default_icon": {
- "16": "favicon-16x16.png",
- "32": "favicon-32x32.png",
- "48": "favicon-48x48.png",
- "128": "favicon-128x128.png"
- }
- }
-}
diff --git a/package.json b/package.json
index da832ad..749ab0b 100644
--- a/package.json
+++ b/package.json
@@ -7,8 +7,7 @@
"main": "index.js",
"scripts": {
"dev": "vite",
- "build_chrome": "tsc && vite build -m production_chrome && node fixChrome.cjs",
- "build_firefox": "tsc && vite build -m production_chrome && node fixFirefox.cjs",
+ "build": "tsc && vite build",
"fix": "eslint --fix --quiet ."
},
"author": "IndieKKY",
@@ -47,6 +46,7 @@
"devDependencies": {
"@tailwindcss/line-clamp": "^0.4.2",
"@tailwindcss/typography": "^0.5.8",
+ "@types/node": "^20.8.10",
"@types/chrome": "^0.0.203",
"@types/js-search": "^1.4.0",
"@types/lodash-es": "^4.17.6",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 2b4162e..1594e9e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -111,6 +111,9 @@ importers:
'@types/lodash-es':
specifier: ^4.17.6
version: 4.17.6
+ '@types/node':
+ specifier: ^20.8.10
+ version: 20.16.10
'@types/pako':
specifier: ^2.0.0
version: 2.0.0
@@ -490,6 +493,9 @@ packages:
'@types/ms@0.7.31':
resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==}
+ '@types/node@20.16.10':
+ resolution: {integrity: sha512-vQUKgWTjEIRFCvK6CyriPH3MZYiYlNy0fKiEYHWbcoWLEgs4opurGGKlebrTLqdSMIbXImH6XExNiIyNUv3WpA==}
+
'@types/pako@2.0.0':
resolution: {integrity: sha512-10+iaz93qR5WYxTo+PMifD5TSxiOtdRaxBf7INGGXMQgTCu8Z/7GYWYFUOS3q/G0nE5boj1r4FEB+WSy7s5gbA==}
@@ -2343,6 +2349,9 @@ packages:
unbox-primitive@1.0.2:
resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==}
+ undici-types@6.19.8:
+ resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==}
+
unified@10.1.2:
resolution: {integrity: sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==}
@@ -2881,6 +2890,10 @@ snapshots:
'@types/ms@0.7.31': {}
+ '@types/node@20.16.10':
+ dependencies:
+ undici-types: 6.19.8
+
'@types/pako@2.0.0': {}
'@types/prop-types@15.7.5': {}
@@ -4955,6 +4968,8 @@ snapshots:
has-symbols: 1.0.3
which-boxed-primitive: 1.0.2
+ undici-types@6.19.8: {}
+
unified@10.1.2:
dependencies:
'@types/unist': 2.0.6
diff --git a/public/popup.html b/popup.html
similarity index 100%
rename from public/popup.html
rename to popup.html
diff --git a/public/bibigpt.png b/public/bibigpt.png
deleted file mode 100644
index 5be4212..0000000
Binary files a/public/bibigpt.png and /dev/null differ
diff --git a/public/immersive-summary.png b/public/immersive-summary.png
deleted file mode 100644
index 59c94fb..0000000
Binary files a/public/immersive-summary.png and /dev/null differ
diff --git a/public/my-article-summarizer.png b/public/my-article-summarizer.png
deleted file mode 100644
index 12cfd8b..0000000
Binary files a/public/my-article-summarizer.png and /dev/null differ
diff --git a/public/openai-up.ico b/public/openai-up.ico
deleted file mode 100644
index fbcfb14..0000000
Binary files a/public/openai-up.ico and /dev/null differ
diff --git a/push.sh b/push.sh
index ec5f47f..9b2a2e4 100755
--- a/push.sh
+++ b/push.sh
@@ -17,13 +17,11 @@ done
# Update the version in package.json
sed -i '' "s/\"version\": \"$current_version\"/\"version\": \"$new_version\"/g" package.json
-# Update the version in manifest.json
-sed -i '' "s/\"version\": \"$current_version\"/\"version\": \"$new_version\"/g" manifest.json
echo "Version updated to: $new_version"
# build
-pnpm run build_chrome
+pnpm run build
# zip dist
rm -f dist.zip
cd dist
diff --git a/src/App.tsx b/src/App.tsx
index af6d03d..3487547 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -75,7 +75,7 @@ function App() {
useTranslateService()
useSearchService()
- return
diff --git a/src/chrome/content-script.cjs b/src/inject/inject.ts
similarity index 84%
rename from src/chrome/content-script.cjs
rename to src/inject/inject.ts
index d478fe3..4579366 100644
--- a/src/chrome/content-script.cjs
+++ b/src/inject/inject.ts
@@ -1,9 +1,9 @@
-const {TOTAL_HEIGHT_DEF, HEADER_HEIGHT, TOTAL_HEIGHT_MIN, TOTAL_HEIGHT_MAX, IFRAME_ID} = require("../const");
-var totalHeight = TOTAL_HEIGHT_DEF
+import {TOTAL_HEIGHT_DEF, HEADER_HEIGHT, TOTAL_HEIGHT_MIN, TOTAL_HEIGHT_MAX, IFRAME_ID} from '@/const'
+let totalHeight = TOTAL_HEIGHT_DEF
const getVideoElement = () => {
const videoWrapper = document.getElementById('bilibili-player')
- return videoWrapper.querySelector('video')
+ return videoWrapper?.querySelector('video') as HTMLVideoElement | undefined
}
const timerIframe = setInterval(function () {
@@ -24,7 +24,10 @@ const timerIframe = setInterval(function () {
const iframe = document.createElement('iframe')
iframe.id = IFRAME_ID
iframe.src = chrome.runtime.getURL('index.html')
- iframe.style = 'border: none; width: 100%; height: 44px;margin-bottom: 3px;'
+ iframe.style.border = 'none'
+ iframe.style.width = '100%'
+ iframe.style.height = '44px'
+ iframe.style.marginBottom = '3px'
iframe.allow = 'clipboard-read; clipboard-write;'
if (vKey) {
iframe.dataset[vKey] = danmukuBox?.dataset[vKey]
@@ -37,19 +40,19 @@ const timerIframe = setInterval(function () {
}
}, 1000)
-let aid = 0
+let aid: number | null = null
let title = ''
-let pages = []
-let pagesMap = {}
+let pages: any[] = []
+let pagesMap: Record = {}
-let lastAidOrBvid = null
+let lastAidOrBvid: string | null = null
const refreshVideoInfo = async () => {
- const iframe = document.getElementById(IFRAME_ID)
+ const iframe = document.getElementById(IFRAME_ID) as HTMLIFrameElement | undefined
if (!iframe) return
// fix: https://github.com/IndieKKY/bilibili-subtitle/issues/5
// 处理稍后再看的url( https://www.bilibili.com/list/watchlater?bvid=xxx&oid=xxx )
- const pathSearchs = {}
+ const pathSearchs: Record = {}
location.search.slice(1).replace(/([^=&]*)=([^=&]*)/g, (matchs, a, b, c) => pathSearchs[a] = b)
// bvid
@@ -72,7 +75,7 @@ const refreshVideoInfo = async () => {
let cid
let subtitles
if (aidOrBvid.toLowerCase().startsWith('av')) {//avxxx
- aid = aidOrBvid.slice(2)
+ aid = parseInt(aidOrBvid.slice(2))
pages = await fetch(`https://api.bilibili.com/x/player/pagelist?aid=${aid}`, {credentials: 'include'}).then(res => res.json()).then(res => res.data)
cid = pages[0].cid
title = pages[0].part
@@ -100,7 +103,7 @@ const refreshVideoInfo = async () => {
console.debug('refreshVideoInfo: ', aid, cid, pages, subtitles)
//send setVideoInfo
- iframe.contentWindow.postMessage({
+ iframe.contentWindow?.postMessage({
type: 'setVideoInfo',
url: location.origin + location.pathname,
title,
@@ -112,10 +115,10 @@ const refreshVideoInfo = async () => {
}
}
-let lastAid = null
-let lastCid = null
+let lastAid: number | null = null
+let lastCid: number | null = null
const refreshSubtitles = () => {
- const iframe = document.getElementById(IFRAME_ID)
+ const iframe = document.getElementById(IFRAME_ID) as HTMLIFrameElement | undefined
if (!iframe) return
const urlSearchParams = new URLSearchParams(window.location.search)
@@ -136,7 +139,7 @@ const refreshSubtitles = () => {
.then(res => res.json())
.then(res => {
// console.log('refreshSubtitles: ', aid, cid, res)
- iframe.contentWindow.postMessage({
+ iframe.contentWindow?.postMessage({
type: 'setInfos',
infos: res.data.subtitle.subtitles
}, '*')
@@ -150,8 +153,10 @@ window.addEventListener("message", (event) => {
const {data} = event
if (data.type === 'fold') {
- const iframe = document.getElementById(IFRAME_ID)
- iframe.style.height = (data.fold ? HEADER_HEIGHT : totalHeight) + 'px'
+ const iframe = document.getElementById(IFRAME_ID) as HTMLIFrameElement | undefined
+ if (iframe) {
+ iframe.style.height = (data.fold ? HEADER_HEIGHT : totalHeight) + 'px'
+ }
}
if (data.type === 'move') {
@@ -178,7 +183,7 @@ window.addEventListener("message", (event) => {
url = url.replace('http://', 'https://')
}
fetch(url).then(res => res.json()).then(res => {
- event.source.postMessage({
+ event.source?.postMessage({
data: {
info: data.info,
data: res,
@@ -190,7 +195,7 @@ window.addEventListener("message", (event) => {
if (data.type === 'getCurrentTime') {
const video = getVideoElement()
if (video) {
- event.source.postMessage({
+ event.source?.postMessage({
data: {
currentTime: video.currentTime
}, type: 'setCurrentTime'
@@ -201,7 +206,7 @@ window.addEventListener("message", (event) => {
if (data.type === 'getSettings') {
const videoElement = getVideoElement()
totalHeight = videoElement ? Math.min(Math.max(videoElement.offsetHeight, TOTAL_HEIGHT_MIN), TOTAL_HEIGHT_MAX) : TOTAL_HEIGHT_DEF
- event.source.postMessage({
+ event.source?.postMessage({
data: {
noVideo: !videoElement,
totalHeight,
@@ -211,7 +216,7 @@ window.addEventListener("message", (event) => {
if (data.type === 'downloadAudio') {
const html = document.getElementsByTagName('html')[0].innerHTML
- const playInfo = JSON.parse(html.match(/window.__playinfo__=(.+?)<\/script/)[1])
+ const playInfo = JSON.parse(html.match(/window.__playinfo__=(.+?)<\/script/)?.[1] ?? '{}')
const audioUrl = playInfo.data.dash.audio[0].baseUrl
fetch(audioUrl).then(res => res.blob()).then(blob => {
diff --git a/src/redux/envReducer.ts b/src/redux/envReducer.ts
index d912939..e7ca207 100644
--- a/src/redux/envReducer.ts
+++ b/src/redux/envReducer.ts
@@ -67,11 +67,11 @@ const initialState: EnvState = {
},
totalHeight: TOTAL_HEIGHT_DEF,
autoScroll: true,
- currentTime: import.meta.env.VITE_ENV === 'web-dev' ? 30 : undefined,
+ // currentTime: import.meta.env.VITE_ENV === 'web-dev' ? 30 : undefined,
envReady: false,
tempReady: false,
fold: true,
- data: import.meta.env.VITE_ENV === 'web-dev' ? getDevData() : undefined,
+ // data: import.meta.env.VITE_ENV === 'web-dev' ? getDevData() : undefined,
transResults: {},
inputting: false,
diff --git a/tsconfig.json b/tsconfig.json
index 9539005..d7dd2fb 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -17,7 +17,10 @@
"jsx": "react-jsx",
"types": [
"@types/chrome"
- ]
+ ],
+ "paths": {
+ "@/*": ["./src/*"]
+ }
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
diff --git a/tsconfig.node.json b/tsconfig.node.json
index e229c97..be6f5d9 100644
--- a/tsconfig.node.json
+++ b/tsconfig.node.json
@@ -6,5 +6,5 @@
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true
},
- "include": ["vite.config.ts", "manifest.json"]
+ "include": ["vite.config.ts", "manifest.config.ts"]
}
diff --git a/vite.config.ts b/vite.config.ts
index 70c859f..fb59aa4 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -2,24 +2,33 @@ import {defineConfig, PluginOption} from 'vite'
import react from '@vitejs/plugin-react'
import {visualizer} from "rollup-plugin-visualizer";
import {crx} from '@crxjs/vite-plugin'
+import path from "path"
// @ts-ignore
import manifest from './manifest.json'
// https://vitejs.dev/config/
export default ({mode}) => {
- const plugins = [
- react(),
- visualizer() as PluginOption,
- ]
- // @ts-ignore
- if (mode === 'production_chrome' || mode === 'production_edge') {
- plugins.push(crx({
- manifest,
- }))
- }
return defineConfig({
base: '/',
- plugins,
+ build: {
+ rollupOptions: {
+ input: {
+ index: 'index.html',
+ },
+ },
+ },
+ resolve: {
+ alias: {
+ '@': path.resolve(__dirname, './src'),
+ }
+ },
+ plugins: [
+ react(),
+ crx({
+ manifest,
+ }),
+ visualizer() as PluginOption,
+ ],
css: {
modules: {
localsConvention: "camelCase"
diff --git a/vite.config.ts.timestamp-1705728659971.mjs b/vite.config.ts.timestamp-1705728659971.mjs
deleted file mode 100644
index d2c3687..0000000
--- a/vite.config.ts.timestamp-1705728659971.mjs
+++ /dev/null
@@ -1,66 +0,0 @@
-// vite.config.ts
-import { defineConfig } from "vite";
-import react from "@vitejs/plugin-react";
-import { visualizer } from "rollup-plugin-visualizer";
-import { crx } from "@crxjs/vite-plugin";
-
-// manifest.json
-var manifest_default = {
- name: "\u54D4\u54E9\u54D4\u54E9\u5B57\u5E55\u5217\u8868",
- description: "\u663E\u793AB\u7AD9\u89C6\u9891\u7684\u5B57\u5E55\u5217\u8868,\u53EF\u70B9\u51FB\u8DF3\u8F6C\u4E0E\u4E0B\u8F7D\u5B57\u5E55,\u5E76\u652F\u6301\u7FFB\u8BD1\u548C\u603B\u7ED3\u5B57\u5E55!",
- version: "1.7.11",
- manifest_version: 3,
- permissions: [
- "storage"
- ],
- background: {
- service_worker: "src/chrome/background.ts"
- },
- content_scripts: [
- {
- matches: ["https://www.bilibili.com/video/*", "https://www.bilibili.com/list/*"],
- js: ["src/chrome/content-script.cjs"]
- }
- ],
- icons: {
- "16": "favicon-16x16.png",
- "32": "favicon-32x32.png",
- "48": "favicon-48x48.png",
- "128": "favicon-128x128.png"
- },
- action: {
- default_popup: "index.html",
- default_icon: {
- "16": "favicon-16x16.png",
- "32": "favicon-32x32.png",
- "48": "favicon-48x48.png",
- "128": "favicon-128x128.png"
- }
- }
-};
-
-// vite.config.ts
-var vite_config_default = ({ mode }) => {
- const plugins = [
- react(),
- visualizer()
- ];
- if (mode === "production_chrome") {
- plugins.push(crx({
- manifest: manifest_default
- }));
- }
- return defineConfig({
- base: "/",
- plugins,
- css: {
- modules: {
- localsConvention: "camelCase"
- }
- }
- });
-};
-export {
- vite_config_default as default
-};
-//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCIvVXNlcnMvZmVuZ3l1ZXhpYW5nL2RhdGEvcHJvamVjdC9iaWxpYmlsaS1zdWJ0aXRsZVwiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9maWxlbmFtZSA9IFwiL1VzZXJzL2Zlbmd5dWV4aWFuZy9kYXRhL3Byb2plY3QvYmlsaWJpbGktc3VidGl0bGUvdml0ZS5jb25maWcudHNcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfaW1wb3J0X21ldGFfdXJsID0gXCJmaWxlOi8vL1VzZXJzL2Zlbmd5dWV4aWFuZy9kYXRhL3Byb2plY3QvYmlsaWJpbGktc3VidGl0bGUvdml0ZS5jb25maWcudHNcIjtpbXBvcnQge2RlZmluZUNvbmZpZywgUGx1Z2luT3B0aW9ufSBmcm9tICd2aXRlJ1xuaW1wb3J0IHJlYWN0IGZyb20gJ0B2aXRlanMvcGx1Z2luLXJlYWN0J1xuaW1wb3J0IHt2aXN1YWxpemVyfSBmcm9tIFwicm9sbHVwLXBsdWdpbi12aXN1YWxpemVyXCI7XG5pbXBvcnQge2NyeH0gZnJvbSAnQGNyeGpzL3ZpdGUtcGx1Z2luJ1xuLy8gQHRzLWlnbm9yZVxuaW1wb3J0IG1hbmlmZXN0IGZyb20gJy4vbWFuaWZlc3QuanNvbidcblxuLy8gaHR0cHM6Ly92aXRlanMuZGV2L2NvbmZpZy9cbmV4cG9ydCBkZWZhdWx0ICh7bW9kZX0pID0+IHtcbiAgY29uc3QgcGx1Z2lucyA9IFtcbiAgICByZWFjdCgpLFxuICAgIHZpc3VhbGl6ZXIoKSBhcyBQbHVnaW5PcHRpb24sXG4gIF1cbiAgLy8gQHRzLWlnbm9yZVxuICBpZiAobW9kZSA9PT0gJ3Byb2R1Y3Rpb25fY2hyb21lJykge1xuICAgIHBsdWdpbnMucHVzaChjcngoe1xuICAgICAgbWFuaWZlc3QsXG4gICAgfSkpXG4gIH1cbiAgcmV0dXJuIGRlZmluZUNvbmZpZyh7XG4gICAgYmFzZTogJy8nLFxuICAgIHBsdWdpbnMsXG4gICAgY3NzOiB7XG4gICAgICBtb2R1bGVzOiB7XG4gICAgICAgIGxvY2Fsc0NvbnZlbnRpb246IFwiY2FtZWxDYXNlXCJcbiAgICAgIH1cbiAgICB9XG4gIH0pXG59XG4iXSwKICAibWFwcGluZ3MiOiAiO0FBQXdVLFNBQVEsb0JBQWlDO0FBQ2pYLE9BQU8sV0FBVztBQUNsQixTQUFRLGtCQUFpQjtBQUN6QixTQUFRLFdBQVU7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBS2xCLElBQU8sc0JBQVEsQ0FBQyxFQUFDLEtBQUksTUFBTTtBQUN6QixRQUFNLFVBQVU7QUFBQSxJQUNkLE1BQU07QUFBQSxJQUNOLFdBQVc7QUFBQSxFQUNiO0FBRUEsTUFBSSxTQUFTLHFCQUFxQjtBQUNoQyxZQUFRLEtBQUssSUFBSTtBQUFBLE1BQ2Y7QUFBQSxJQUNGLENBQUMsQ0FBQztBQUFBLEVBQ0o7QUFDQSxTQUFPLGFBQWE7QUFBQSxJQUNsQixNQUFNO0FBQUEsSUFDTjtBQUFBLElBQ0EsS0FBSztBQUFBLE1BQ0gsU0FBUztBQUFBLFFBQ1Asa0JBQWtCO0FBQUEsTUFDcEI7QUFBQSxJQUNGO0FBQUEsRUFDRixDQUFDO0FBQ0g7IiwKICAibmFtZXMiOiBbXQp9Cg==