diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 4b7dd48..2e8840a 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -1,11 +1,12 @@ module.exports = { root: true, extends: [ - '@pionxzh/eslint-config-ts', + '@pionxzh/eslint-config-react', ], rules: { 'no-console': 'off', 'no-alert': 'off', + 'react/prop-types': 'off', '@typescript-eslint/ban-ts-comment': 'off', '@typescript-eslint/no-unused-vars': 'off', }, diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..3e775ef --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +auto-install-peers=true diff --git a/package.json b/package.json index e61600a..b37ffe8 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "devDependencies": { "@commitlint/cli": "^17.3.0", "@commitlint/config-conventional": "^17.3.0", - "@pionxzh/eslint-config-ts": "^0.1.1", + "@pionxzh/eslint-config-react": "^0.1.1", "@types/node": "^18.11.17", "cpy-cli": "^4.2.0", "eslint": "^8.30.0", diff --git a/packages/userscript/package.json b/packages/userscript/package.json index 9628e8e..d456211 100644 --- a/packages/userscript/package.json +++ b/packages/userscript/package.json @@ -17,10 +17,12 @@ }, "dependencies": { "html2canvas": "^1.4.1", + "preact": "^10.11.3", "sentinel-js": "^0.0.5", "vite-plugin-monkey": "^2.10.0" }, "devDependencies": { + "@preact/preset-vite": "^2.5.0", "vite": "^4.0.3" } } diff --git a/packages/userscript/src/exporter/html.ts b/packages/userscript/src/exporter/html.ts new file mode 100644 index 0000000..f83efcf --- /dev/null +++ b/packages/userscript/src/exporter/html.ts @@ -0,0 +1,79 @@ +import { ChatGPTAvatar } from '../icons' +import { getConversation } from '../parser' +import { timestamp } from '../utils/utils' +import { downloadFile } from '../utils/download' + +import templateHtml from '../template.html?raw' + +export function exportToHtml() { + const conversations = getConversation() + if (conversations.length === 0) return alert('No conversation found. Please send a message first.') + + const lang = document.documentElement.lang ?? 'en' + const conversationHtml = conversations.map((item) => { + const { author: { name, avatar }, lines } = item + + const avatarEl = name === 'ChatGPT' + ? `${ChatGPTAvatar}` + : `${name}` + + const linesHtml = lines.map((line) => { + const lineHtml = line.map((item) => { + switch (item.type) { + case 'text': + return escapeHtml(item.text) + case 'image': + return `` + case 'code': + return `${escapeHtml(item.code)}` + case 'code-block': + return `
${escapeHtml(item.code)}
` + case 'link': + return `${escapeHtml(item.text)}` + case 'ordered-list-item': + return `
    ${item.items.map(item => `
  1. ${escapeHtml(item)}
  2. `).join('')}
` + case 'unordered-list-item': + return `` + case 'table': { + const header = item.headers.map(item => `${escapeHtml(item)}`).join('') + const body = item.rows.map(row => `${row.map(item => `${escapeHtml(item)}`).join('')}`).join('') + return `${header}${body}
` + } + default: + return '' + } + }).join('') + + const skipTags = ['pre', 'ul', 'ol', 'table'] + if (skipTags.some(tag => lineHtml.startsWith(`<${tag}>`))) return lineHtml + return `

${lineHtml}

` + }).join('') + + return ` +
+
+ ${avatarEl} +
+
+${linesHtml} +
+
` + }).join('') + + const html = templateHtml + .replace('{{time}}', new Date().toISOString()) + .replace('{{lang}}', lang) + .replace('{{content}}', conversationHtml) + + const fileName = `ChatGPT-${timestamp()}.html` + downloadFile(fileName, 'text/html', html) +} + +function escapeHtml(html: string) { + return html + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, ''') +} diff --git a/packages/userscript/src/exporter/image.ts b/packages/userscript/src/exporter/image.ts new file mode 100644 index 0000000..7234985 --- /dev/null +++ b/packages/userscript/src/exporter/image.ts @@ -0,0 +1,28 @@ +import html2canvas from 'html2canvas' +import { downloadUrl } from '../utils/download' +import { sleep, timestamp } from '../utils/utils' + +export async function exportToPng() { + const thread = document.querySelector('main .group')?.parentElement as HTMLElement + if (!thread || thread.children.length === 0) return + + // hide bottom bar + thread.children[thread.children.length - 1].classList.add('hidden') + + await sleep(100) + + const canvas = await html2canvas(thread, { + scrollX: -window.scrollX, + scrollY: -window.scrollY, + windowWidth: thread.scrollWidth, + windowHeight: thread.scrollHeight, + }) + + // restore the layout + thread.children[thread.children.length - 1].classList.remove('hidden') + + const dataUrl = canvas.toDataURL('image/png', 1) + .replace(/^data:image\/[^;]/, 'data:application/octet-stream') + const fileName = `ChatGPT-${timestamp()}.png` + downloadUrl(fileName, dataUrl) +} diff --git a/packages/userscript/src/exporter/markdown.ts b/packages/userscript/src/exporter/markdown.ts new file mode 100644 index 0000000..63ccf09 --- /dev/null +++ b/packages/userscript/src/exporter/markdown.ts @@ -0,0 +1,21 @@ +import { getConversation } from '../parser' +import type { Conversation } from '../type' +import { downloadFile } from '../utils/download' +import { timestamp } from '../utils/utils' +import { lineToText } from './text' + +export function exportToMarkdown() { + const conversations = getConversation() + if (conversations.length === 0) return alert('No conversation found. Please send a message first.') + + const text = conversationToMarkdown(conversations) + downloadFile(`chatgpt-${timestamp()}.md`, 'text/markdown', text) +} + +function conversationToMarkdown(conversation: Conversation[]) { + return conversation.map((item) => { + const { author: { name }, lines } = item + const text = lines.map(line => lineToText(line)).join('\r\n\r\n') + return `#### ${name}:\r\n${text}` + }).join('\r\n\r\n') +} diff --git a/packages/userscript/src/exporter/text.ts b/packages/userscript/src/exporter/text.ts new file mode 100644 index 0000000..f86ef22 --- /dev/null +++ b/packages/userscript/src/exporter/text.ts @@ -0,0 +1,33 @@ +import type { ConversationLine } from '../type' +import { getConversation } from '../parser' +import { copyToClipboard } from '../utils/clipboard' +import { tableToMarkdown } from '../utils/markdown' + +export function exportToText() { + const conversations = getConversation() + if (conversations.length === 0) return alert('No conversation found. Please send a message first.') + + const text = conversations.map((item) => { + const { author: { name }, lines } = item + const text = lines.map(line => lineToText(line)).join('\r\n\r\n') + return `${name}:\r\n${text}` + }).join('\r\n\r\n') + + copyToClipboard(text) +} + +export function lineToText(line: ConversationLine): string { + return line.map((item) => { + switch (item.type) { + case 'text': return item.text + case 'image': return '[image]' + case 'link': return `[${item.text}](${item.href})` + case 'ordered-list-item': return item.items.map((item, index) => `${index + 1}. ${item}`).join('\r\n') + case 'unordered-list-item': return item.items.map(item => `- ${item}`).join('\r\n') + case 'code': return `\`${item.code}\`` + case 'code-block': return `\`\`\`${item.lang}\r\n${item.code}\`\`\`` + case 'table': return tableToMarkdown(item.headers, item.rows) + default: return '' + } + }).join('') +} diff --git a/packages/userscript/src/icons.ts b/packages/userscript/src/icons.ts deleted file mode 100644 index 4815873..0000000 --- a/packages/userscript/src/icons.ts +++ /dev/null @@ -1,19 +0,0 @@ -// source: fontawesome: file-lines -export const fileLines = '' - -// source: fontawesome: file-code -export const fileCode = '' - -// source: fontawesome: file-image -export const iconCamera = '' - -// source: fontawesome: markdown -export const iconMarkdown = '' - -// source: fontawesome: copy -export const iconCopy = '' - -// source: fontawesome: arrow-right-from-bracket -export const iconArrowRightFromBracket = '' - -export const chatGPTAvatarSVG = '' diff --git a/packages/userscript/src/icons.tsx b/packages/userscript/src/icons.tsx new file mode 100644 index 0000000..15f5329 --- /dev/null +++ b/packages/userscript/src/icons.tsx @@ -0,0 +1,27 @@ +// source: fontawesome: file-code +export function FileCode() { + return {/* */} +} + +// source: fontawesome: file-image +export function IconCamera() { + return {/* */} +} + +// source: fontawesome: markdown +export function IconMarkdown() { + return {/* */} +} + +// source: fontawesome: copy +export function IconCopy() { + return {/* */} +} + +// source: fontawesome: arrow-right-from-bracket +export function IconArrowRightFromBracket() { + return {/* */} +} + +export const ChatGPTAvatar = '' + diff --git a/packages/userscript/src/main.ts b/packages/userscript/src/main.ts deleted file mode 100644 index 7f8362f..0000000 --- a/packages/userscript/src/main.ts +++ /dev/null @@ -1,370 +0,0 @@ -import html2canvas from 'html2canvas' -import sentinel from 'sentinel-js' -import { chatGPTAvatarSVG, fileCode, iconArrowRightFromBracket, iconCamera, iconCopy, iconMarkdown } from './icons' -import { copyToClipboard, downloadFile, downloadUrl, escapeHtml, getBase64FromImg, onloadSafe, sleep, timestamp } from './utils' -import templateHtml from './template.html?raw' - -import './style.css' - -type ConversationLineNode = | -{ type: 'text'; text: string } | -{ type: 'image'; src: string } | -{ type: 'code'; code: string } | -{ type: 'code-block'; lang: string; code: string } | -{ type: 'link'; text: string; href: string } | -{ type: 'ordered-list-item'; items: string[] } | -{ type: 'unordered-list-item'; items: string[] } | -{ type: 'table'; headers: string[]; rows: string[][] } - -type ConversationLine = ConversationLineNode[] - -interface Conversation { - author: { - name: string - avatar: string - } - lines: ConversationLine[] -} - -main() - -function main() { - onloadSafe(() => { - const copyHtml = `${iconCopy}Copy Text` - const copiedHtml = `${iconCopy}Copied` - const onCopyText = (e: MouseEvent) => { - const items = getConversation() - if (items.length === 0) return alert('No conversation found. Please send a message first.') - - const text = conversationToText(items) - copyToClipboard(text) - - const menuItem = e.target as HTMLAnchorElement - menuItem.innerHTML = copiedHtml - setTimeout(() => { - menuItem.innerHTML = copyHtml - }, 3000) - } - - const divider = createDivider() - const exportButton = createMenuItem(iconArrowRightFromBracket, 'Export', () => {}) - const dropdown = document.createElement('div') - dropdown.classList.add('dropdown-menu', 'bg-gray-900') - - const textExport = createMenuItem(iconCopy, 'Copy Text', onCopyText) - const pngExport = createMenuItem(iconCamera, 'Screenshot', exportToPng) - const mdExport = createMenuItem(iconMarkdown, 'Markdown', exportToMarkdown) - const htmlExport = createMenuItem(fileCode, 'WebPage (HTML)', exportToHtml) - const container = createMenuContainer() - dropdown.append(textExport, pngExport, mdExport, htmlExport) - container.append(exportButton, dropdown, divider) - }) -} - -function createMenuContainer() { - const container = document.createElement('div') - container.id = 'exporter-menu' - container.className = 'pt-1 relative' - - sentinel.on('nav', (nav) => { - const chatList = document.querySelector('nav > div.overflow-y-auto') - if (chatList) { - chatList.after(container) - } - else { - nav.append(container) - } - }) - - return container -} - -function createDivider() { - const divider = document.createElement('div') - divider.className = 'border-b border-white/20' - return divider -} - -function createMenuItem(icon: string, title: string, onClick: (e: MouseEvent) => void) { - const menuItem = document.createElement('a') - menuItem.className = 'flex py-3 px-3 items-center gap-3 rounded-md hover:bg-gray-500/10 transition-colors duration-200 text-white cursor-pointer text-sm mb-2 flex-shrink-0 border border-white/20' - menuItem.removeAttribute('href') - menuItem.innerHTML = `${icon}${title}` - menuItem.addEventListener('click', onClick) - - return menuItem -} - -function exportToMarkdown() { - const items = getConversation() - if (items.length === 0) return alert('No conversation found. Please send a message first.') - - const text = conversationToMarkdown(items) - downloadFile(`chatgpt-${timestamp()}.md`, 'text/markdown', text) -} - -function exportToHtml() { - const items = getConversation() - if (items.length === 0) return alert('No conversation found. Please send a message first.') - - const lang = document.documentElement.lang ?? 'en' - const conversationHtml = items.map((item) => { - const { author: { name, avatar }, lines } = item - - const avatarEl = name === 'ChatGPT' - ? `${chatGPTAvatarSVG}` - : `${name}` - - const linesHtml = lines.map((line) => { - const lineHtml = line.map((item) => { - switch (item.type) { - case 'text': - return escapeHtml(item.text) - case 'image': - return `` - case 'code': - return `${escapeHtml(item.code)}` - case 'code-block': - return `
${escapeHtml(item.code)}
` - case 'link': - return `${escapeHtml(item.text)}` - case 'ordered-list-item': - return `
    ${item.items.map(item => `
  1. ${escapeHtml(item)}
  2. `).join('')}
` - case 'unordered-list-item': - return `` - case 'table': { - const header = item.headers.map(item => `${escapeHtml(item)}`).join('') - const body = item.rows.map(row => `${row.map(item => `${escapeHtml(item)}`).join('')}`).join('') - return `${header}${body}
` - } - default: - return '' - } - }).join('') - - const skipTags = ['pre', 'ul', 'ol', 'table'] - if (skipTags.some(tag => lineHtml.startsWith(`<${tag}>`))) return lineHtml - return `

${lineHtml}

` - }).join('') - - return ` -
-
- ${avatarEl} -
-
-${linesHtml} -
-
` - }).join('') - - const html = templateHtml - .replace('{{time}}', new Date().toISOString()) - .replace('{{lang}}', lang) - .replace('{{content}}', conversationHtml) - - const fileName = `ChatGPT-${timestamp()}.html` - downloadFile(fileName, 'text/html', html) -} - -async function exportToPng() { - const thread = document.querySelector('main .group')?.parentElement as HTMLElement - if (!thread || thread.children.length === 0) return - - // hide bottom bar - thread.children[thread.children.length - 1].classList.add('hidden') - - await sleep(100) - - const canvas = await html2canvas(thread, { - scrollX: -window.scrollX, - scrollY: -window.scrollY, - windowWidth: thread.scrollWidth, - windowHeight: thread.scrollHeight, - }) - - // restore the layout - thread.children[thread.children.length - 1].classList.remove('hidden') - - const dataUrl = canvas.toDataURL('image/png', 1) - .replace(/^data:image\/[^;]/, 'data:application/octet-stream') - const fileName = `ChatGPT-${timestamp()}.png` - downloadUrl(fileName, dataUrl) -} - -function getConversation(): Conversation[] { - const items: Conversation[] = [] - document.querySelectorAll('main .group').forEach((item) => { - const avatarEl = item.querySelector('span img:not([aria-hidden="true"])') - // actually we can get the name from the avatar's alt - // but let's keep it anonymous for privacy reasons - const name = avatarEl?.getAttribute('alt') ? 'You' : 'ChatGPT' - const avatar = avatarEl ? getBase64FromImg(avatarEl) : '' - - const textNode = item.querySelector('.markdown') ?? item.querySelector('.w-full .whitespace-pre-wrap') - if (!textNode) return - - const lines = parseTextNode(textNode) - items.push({ author: { name, avatar }, lines }) - }) - - return items -} - -function parseTextNode(textNode: HTMLDivElement): ConversationLine[] { - const warningClassName = 'bg-orange-500/10' - const dangerClassName = 'bg-red-500/10' - - const childNodes = textNode.childNodes ? Array.from(textNode.childNodes) : [] - const validChildNodes = childNodes.filter((c) => { - if (c instanceof Text) return true - - // filter out the alert box - if (c instanceof Element) { - return !(c.classList.contains(warningClassName) || c.classList.contains(dangerClassName)) - } - - // other nodes are not supported - return false - }) - if (validChildNodes.length === 0) return [[{ type: 'text', text: textNode.textContent ?? '' }]] - if (validChildNodes.length === 1 && validChildNodes[0] instanceof Text) return [[{ type: 'text', text: validChildNodes[0].textContent ?? '' }]] - - const lines: ConversationLine[] = [] - Array.from(textNode.children).forEach((child) => { - if (child.classList.contains(warningClassName)) return - if (child.classList.contains(dangerClassName)) return - - switch (child.tagName.toUpperCase()) { - case 'PRE': { - const codeEl = child.querySelector('code') - if (codeEl) { - const code = codeEl.textContent ?? '' - const classList = Array.from(codeEl.classList) - const lang = classList.find(c => c.startsWith('language-'))?.replace('language-', '') ?? '' - lines.push([{ type: 'code-block', lang, code }]) - } - break - } - case 'OL': { - const items = Array.from(child.children).map(item => item.textContent ?? '') - lines.push([{ type: 'ordered-list-item', items }]) - break - } - case 'UL': { - const items = Array.from(child.children).map(item => item.textContent ?? '') - lines.push([{ type: 'unordered-list-item', items }]) - break - } - case 'TABLE': { - const headers = Array.from(child.querySelector('thead tr')?.children ?? []).map(item => item.textContent ?? '') - const rows = Array.from(child.querySelector('tbody')?.children ?? []).map(row => Array.from(row.children).map(item => item.textContent ?? '')) - lines.push([{ type: 'table', headers, rows }]) - break - } - case 'P': - default: { - const line: ConversationLine = [] - const nodes = Array.from(child.childNodes) - if (nodes.length === 0) { - const text = child.textContent ?? '' - line.push({ type: 'text', text }) - } - else { - nodes.forEach((item) => { - switch (item.nodeType) { - // code - case 1: { - // detect is it a link - if ('href' in item) { - const href = (item).getAttribute('href') ?? '' - const text = item.textContent ?? href - line.push({ type: 'link', text, href }) - } - // detect is it an image - else if ((item).tagName?.toUpperCase() === 'IMG') { - const src = (item).getAttribute('src') ?? '' - line.push({ type: 'image', src }) - } - else { - const text = item.textContent ?? '' - line.push({ type: 'code', code: text }) - } - break - } - // text - case 3: - default: { - const text = item.textContent ?? '' - line.push({ type: 'text', text }) - break - } - } - }) - } - lines.push(line) - break - } - } - }) - - return lines -} - -function conversationToText(conversation: Conversation[]) { - return conversation.map((item) => { - const { author: { name }, lines } = item - const text = lines.map(line => lineToText(line)).join('\r\n\r\n') - return `${name}:\r\n${text}` - }).join('\r\n\r\n') -} - -function conversationToMarkdown(conversation: Conversation[]) { - return conversation.map((item) => { - const { author: { name }, lines } = item - const text = lines.map(line => lineToText(line)).join('\r\n\r\n') - return `#### ${name}:\r\n${text}` - }).join('\r\n\r\n') -} - -function lineToText(line: ConversationLine): string { - return line.map((item) => { - switch (item.type) { - case 'text': return item.text - case 'image': return '[image]' - case 'link': return `[${item.text}](${item.href})` - case 'ordered-list-item': return item.items.map((item, index) => `${index + 1}. ${item}`).join('\r\n') - case 'unordered-list-item': return item.items.map(item => `- ${item}`).join('\r\n') - case 'code': return `\`${item.code}\`` - case 'code-block': return `\`\`\`${item.lang}\r\n${item.code}\`\`\`` - case 'table': return transformTableToMarkdown(item.headers, item.rows) - default: return '' - } - }).join('') -} - -function transformTableToMarkdown(headers: string[], rows: string[][]): string { - let markdown = '' - - // Find the maximum width of each column - const columnWidths: number[] = [] - for (let i = 0; i < headers.length; i++) { - let maxWidth = headers[i].length - rows.forEach((row) => { - maxWidth = Math.max(maxWidth, row[i].length) - }) - columnWidths.push(maxWidth) - } - - // Add the headers - markdown += `${headers.map((header, i) => header.padEnd(columnWidths[i])).join(' | ')}\n` - markdown += `${headers.map((_header, i) => '-'.repeat(columnWidths[i])).join(' | ')}\n` - - // Add the rows - rows.forEach((row) => { - markdown += `${row.map((cell, i) => cell.padEnd(columnWidths[i])).join(' | ')}\n` - }) - - return markdown -} - diff --git a/packages/userscript/src/main.tsx b/packages/userscript/src/main.tsx new file mode 100644 index 0000000..7f42591 --- /dev/null +++ b/packages/userscript/src/main.tsx @@ -0,0 +1,24 @@ +import sentinel from 'sentinel-js' +import { render } from 'preact' +import { Menu } from './menu' +import { onloadSafe } from './utils/utils' + +main() + +function main() { + onloadSafe(() => { + const container = document.createElement('div') + render(, container) + + sentinel.on('nav', (nav) => { + const chatList = document.querySelector('nav > div.overflow-y-auto') + if (chatList) { + chatList.after(container) + } + else { + // fallback to the bottom of the nav + nav.append(container) + } + }) + }) +} diff --git a/packages/userscript/src/menu.tsx b/packages/userscript/src/menu.tsx new file mode 100644 index 0000000..ab96d16 --- /dev/null +++ b/packages/userscript/src/menu.tsx @@ -0,0 +1,63 @@ +import type { FunctionComponent } from 'preact' +import { exportToText } from './exporter/text' +import { exportToPng } from './exporter/image' +import { exportToMarkdown } from './exporter/markdown' +import { exportToHtml } from './exporter/html' +import { FileCode, IconArrowRightFromBracket, IconCamera, IconCopy, IconMarkdown } from './icons' +import './style.css' + +type FC

= FunctionComponent

+ +interface MenuItemProps { + onClick?: () => void +} +const MenuItem: FC = ({ children, onClick }) => { + return ( +

+ {children} +
+ ) +} + +const Dropdown: FC = ({ children }) => { + return ( +
+ {children} +
+ ) +} + +const Divider = () =>
+ +export function Menu() { + return ( +
+ + + Export + + + + + Copy Text + + + + Screenshot + + + + Markdown + + + + WebPage (HTML) + + + +
+ ) +} diff --git a/packages/userscript/src/parser.ts b/packages/userscript/src/parser.ts new file mode 100644 index 0000000..ee32b71 --- /dev/null +++ b/packages/userscript/src/parser.ts @@ -0,0 +1,130 @@ +import type { Conversation, ConversationLine } from './type' + +export function getConversation(): Conversation[] { + const items: Conversation[] = [] + document.querySelectorAll('main .group').forEach((item) => { + const avatarEl = item.querySelector('span img:not([aria-hidden="true"])') + // actually we can get the name from the avatar's alt + // but let's keep it anonymous for privacy reasons + const name = avatarEl?.getAttribute('alt') ? 'You' : 'ChatGPT' + const avatar = avatarEl ? getBase64FromImg(avatarEl) : '' + + const textNode = item.querySelector('.markdown') ?? item.querySelector('.w-full .whitespace-pre-wrap') + if (!textNode) return + + const lines = parseTextNode(textNode) + items.push({ author: { name, avatar }, lines }) + }) + + return items +} + +function parseTextNode(textNode: HTMLDivElement): ConversationLine[] { + const warningClassName = 'bg-orange-500/10' + const dangerClassName = 'bg-red-500/10' + + const childNodes = textNode.childNodes ? Array.from(textNode.childNodes) : [] + const validChildNodes = childNodes.filter((c) => { + if (c instanceof Text) return true + + // filter out the alert box + if (c instanceof Element) { + return !(c.classList.contains(warningClassName) || c.classList.contains(dangerClassName)) + } + + // other nodes are not supported + return false + }) + if (validChildNodes.length === 0) return [[{ type: 'text', text: textNode.textContent ?? '' }]] + if (validChildNodes.length === 1 && validChildNodes[0] instanceof Text) return [[{ type: 'text', text: validChildNodes[0].textContent ?? '' }]] + + const lines: ConversationLine[] = [] + Array.from(textNode.children).forEach((child) => { + if (child.classList.contains(warningClassName)) return + if (child.classList.contains(dangerClassName)) return + + switch (child.tagName.toUpperCase()) { + case 'PRE': { + const codeEl = child.querySelector('code') + if (codeEl) { + const code = codeEl.textContent ?? '' + const classList = Array.from(codeEl.classList) + const lang = classList.find(c => c.startsWith('language-'))?.replace('language-', '') ?? '' + lines.push([{ type: 'code-block', lang, code }]) + } + break + } + case 'OL': { + const items = Array.from(child.children).map(item => item.textContent ?? '') + lines.push([{ type: 'ordered-list-item', items }]) + break + } + case 'UL': { + const items = Array.from(child.children).map(item => item.textContent ?? '') + lines.push([{ type: 'unordered-list-item', items }]) + break + } + case 'TABLE': { + const headers = Array.from(child.querySelector('thead tr')?.children ?? []).map(item => item.textContent ?? '') + const rows = Array.from(child.querySelector('tbody')?.children ?? []).map(row => Array.from(row.children).map(item => item.textContent ?? '')) + lines.push([{ type: 'table', headers, rows }]) + break + } + case 'P': + default: { + const line: ConversationLine = [] + const nodes = Array.from(child.childNodes) + if (nodes.length === 0) { + const text = child.textContent ?? '' + line.push({ type: 'text', text }) + } + else { + nodes.forEach((item) => { + switch (item.nodeType) { + // code + case 1: { + // detect is it a link + if ('href' in item) { + const href = (item as HTMLAnchorElement).getAttribute('href') ?? '' + const text = item.textContent ?? href + line.push({ type: 'link', text, href }) + } + // detect is it an image + else if ((item as HTMLImageElement).tagName?.toUpperCase() === 'IMG') { + const src = (item as HTMLImageElement).getAttribute('src') ?? '' + line.push({ type: 'image', src }) + } + else { + const text = item.textContent ?? '' + line.push({ type: 'code', code: text }) + } + break + } + // text + case 3: + default: { + const text = item.textContent ?? '' + line.push({ type: 'text', text }) + break + } + } + }) + } + lines.push(line) + break + } + } + }) + + return lines +} + +function getBase64FromImg(el: HTMLImageElement) { + const canvas = document.createElement('canvas') + canvas.width = el.naturalWidth + canvas.height = el.naturalHeight + const ctx = canvas.getContext('2d') + if (!ctx) return '' + ctx.drawImage(el, 0, 0) + return canvas.toDataURL('image/png') +} diff --git a/packages/userscript/src/type.ts b/packages/userscript/src/type.ts new file mode 100644 index 0000000..2120495 --- /dev/null +++ b/packages/userscript/src/type.ts @@ -0,0 +1,19 @@ +export type ConversationLineNode = | +{ type: 'text'; text: string } | +{ type: 'image'; src: string } | +{ type: 'code'; code: string } | +{ type: 'code-block'; lang: string; code: string } | +{ type: 'link'; text: string; href: string } | +{ type: 'ordered-list-item'; items: string[] } | +{ type: 'unordered-list-item'; items: string[] } | +{ type: 'table'; headers: string[]; rows: string[][] } + +export type ConversationLine = ConversationLineNode[] + +export interface Conversation { + author: { + name: string + avatar: string + } + lines: ConversationLine[] +} diff --git a/packages/userscript/src/utils.ts b/packages/userscript/src/utils.ts deleted file mode 100644 index 58b8ec0..0000000 --- a/packages/userscript/src/utils.ts +++ /dev/null @@ -1,70 +0,0 @@ -export function onloadSafe(fn: () => void) { - if (document.readyState === 'complete') { - fn() - } - else { - window.addEventListener('load', fn) - } -} - -export function copyToClipboard(text: string) { - try { - // modern browsers - navigator.clipboard.writeText(text) - } - catch { - const textarea = document.createElement('textarea') - textarea.value = text - document.body.appendChild(textarea) - textarea.select() - document.execCommand('copy') - document.body.removeChild(textarea) - } -} - -export function downloadFile(filename: string, type: string, content: string) { - const blob = new Blob([content], { type }) - const url = URL.createObjectURL(blob) - const a = document.createElement('a') - a.href = url - a.download = filename - document.body.appendChild(a) - a.click() - document.body.removeChild(a) -} - -export function downloadUrl(filename: string, url: string) { - const a = document.createElement('a') - a.href = url - a.download = filename - document.body.appendChild(a) - a.click() - document.body.removeChild(a) -} - -export function getBase64FromImg(el: HTMLImageElement) { - const canvas = document.createElement('canvas') - canvas.width = el.naturalWidth - canvas.height = el.naturalHeight - const ctx = canvas.getContext('2d') - if (!ctx) return '' - ctx.drawImage(el, 0, 0) - return canvas.toDataURL('image/png') -} - -export function sleep(ms: number) { - return new Promise(resolve => setTimeout(resolve, ms)) -} - -export function timestamp() { - return new Date().toISOString().replace(/:/g, '-').replace(/\..+/, '') -} - -export function escapeHtml(html: string) { - return html - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"') - .replace(/'/g, ''') -} diff --git a/packages/userscript/src/utils/clipboard.ts b/packages/userscript/src/utils/clipboard.ts new file mode 100644 index 0000000..a3c12f2 --- /dev/null +++ b/packages/userscript/src/utils/clipboard.ts @@ -0,0 +1,14 @@ +export function copyToClipboard(text: string) { + try { + // for modern browsers + navigator.clipboard.writeText(text) + } + catch { + const textarea = document.createElement('textarea') + textarea.value = text + document.body.appendChild(textarea) + textarea.select() + document.execCommand('copy') + document.body.removeChild(textarea) + } +} diff --git a/packages/userscript/src/utils/download.ts b/packages/userscript/src/utils/download.ts new file mode 100644 index 0000000..0586d2c --- /dev/null +++ b/packages/userscript/src/utils/download.ts @@ -0,0 +1,19 @@ +export function downloadFile(filename: string, type: string, content: string) { + const blob = new Blob([content], { type }) + const url = URL.createObjectURL(blob) + const a = document.createElement('a') + a.href = url + a.download = filename + document.body.appendChild(a) + a.click() + document.body.removeChild(a) +} + +export function downloadUrl(filename: string, url: string) { + const a = document.createElement('a') + a.href = url + a.download = filename + document.body.appendChild(a) + a.click() + document.body.removeChild(a) +} diff --git a/packages/userscript/src/utils/markdown.ts b/packages/userscript/src/utils/markdown.ts new file mode 100644 index 0000000..c7155a7 --- /dev/null +++ b/packages/userscript/src/utils/markdown.ts @@ -0,0 +1,24 @@ +export function tableToMarkdown(headers: string[], rows: string[][]): string { + let markdown = '' + + // Find the maximum width of each column + const columnWidths: number[] = [] + for (let i = 0; i < headers.length; i++) { + let maxWidth = headers[i].length + rows.forEach((row) => { + maxWidth = Math.max(maxWidth, row[i].length) + }) + columnWidths.push(maxWidth) + } + + // Add the headers + markdown += `${headers.map((header, i) => header.padEnd(columnWidths[i])).join(' | ')}\n` + markdown += `${headers.map((_header, i) => '-'.repeat(columnWidths[i])).join(' | ')}\n` + + // Add the rows + rows.forEach((row) => { + markdown += `${row.map((cell, i) => cell.padEnd(columnWidths[i])).join(' | ')}\n` + }) + + return markdown +} diff --git a/packages/userscript/src/utils/utils.ts b/packages/userscript/src/utils/utils.ts new file mode 100644 index 0000000..dd94608 --- /dev/null +++ b/packages/userscript/src/utils/utils.ts @@ -0,0 +1,17 @@ +export function onloadSafe(fn: () => void) { + if (document.readyState === 'complete') { + fn() + } + else { + window.addEventListener('load', fn) + } +} + +export function sleep(ms: number) { + return new Promise(resolve => setTimeout(resolve, ms)) +} + +export function timestamp() { + return new Date().toISOString().replace(/:/g, '-').replace(/\..+/, '') +} + diff --git a/packages/userscript/tsconfig.json b/packages/userscript/tsconfig.json index b30f2cf..9742ebb 100644 --- a/packages/userscript/tsconfig.json +++ b/packages/userscript/tsconfig.json @@ -2,7 +2,9 @@ "extends": "../../tsconfig.base.json", "compilerOptions": { "composite": true, - "types": ["vite/client"] + "types": ["vite/client"], + "jsx": "react-jsx", + "jsxImportSource": "preact", }, "include": [ "src", diff --git a/packages/userscript/vite.config.ts b/packages/userscript/vite.config.ts index 789fbe4..cfe69db 100644 --- a/packages/userscript/vite.config.ts +++ b/packages/userscript/vite.config.ts @@ -1,5 +1,6 @@ import { defineConfig } from 'vite' import monkey from 'vite-plugin-monkey' +import preact from '@preact/preset-vite' import packageJson from './package.json' // https://vitejs.dev/config/ @@ -9,8 +10,12 @@ export default defineConfig({ charset: 'utf8', }, plugins: [ + preact({ + devToolsEnabled: false, + devtoolsInProd: false, + }), monkey({ - entry: 'src/main.ts', + entry: 'src/main.tsx', userscript: { 'name': packageJson.title, 'author': packageJson.author, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3d9770a..b795456 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,7 +11,7 @@ importers: specifiers: '@commitlint/cli': ^17.3.0 '@commitlint/config-conventional': ^17.3.0 - '@pionxzh/eslint-config-ts': ^0.1.1 + '@pionxzh/eslint-config-react': ^0.1.1 '@types/node': ^18.11.17 cpy-cli: ^4.2.0 eslint: ^8.30.0 @@ -23,7 +23,7 @@ importers: devDependencies: '@commitlint/cli': 17.3.0 '@commitlint/config-conventional': 17.3.0 - '@pionxzh/eslint-config-ts': 0.1.1_lzzuuodtsqwxnvqeq4g4likcqa + '@pionxzh/eslint-config-react': 0.1.1_lzzuuodtsqwxnvqeq4g4likcqa '@types/node': 18.11.17 cpy-cli: 4.2.0 eslint: 8.30.0 @@ -35,19 +35,31 @@ importers: packages/userscript: specifiers: + '@preact/preset-vite': ^2.5.0 html2canvas: ^1.4.1 + preact: ^10.11.3 sentinel-js: ^0.0.5 vite: ^4.0.3 vite-plugin-monkey: ^2.10.0 dependencies: html2canvas: 1.4.1 + preact: 10.11.3 sentinel-js: 0.0.5_4xqsksf5w62wic46kncafprj3e vite-plugin-monkey: 2.10.0_vite@4.0.3 devDependencies: + '@preact/preset-vite': 2.5.0_preact@10.11.3+vite@4.0.3 vite: 4.0.3 packages: + /@ampproject/remapping/2.2.0: + resolution: {integrity: sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/gen-mapping': 0.1.1 + '@jridgewell/trace-mapping': 0.3.9 + dev: true + /@babel/code-frame/7.18.6: resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==} engines: {node: '>=6.9.0'} @@ -55,11 +67,152 @@ packages: '@babel/highlight': 7.18.6 dev: true + /@babel/compat-data/7.20.10: + resolution: {integrity: sha512-sEnuDPpOJR/fcafHMjpcpGN5M2jbUGUHwmuWKM/YdPzeEDJg8bgmbcWQFUfE32MQjti1koACvoPVsDe8Uq+idg==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/core/7.20.12: + resolution: {integrity: sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==} + engines: {node: '>=6.9.0'} + dependencies: + '@ampproject/remapping': 2.2.0 + '@babel/code-frame': 7.18.6 + '@babel/generator': 7.20.7 + '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.20.12 + '@babel/helper-module-transforms': 7.20.11 + '@babel/helpers': 7.20.7 + '@babel/parser': 7.20.7 + '@babel/template': 7.20.7 + '@babel/traverse': 7.20.12 + '@babel/types': 7.20.7 + convert-source-map: 1.9.0 + debug: 4.3.4 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/generator/7.20.7: + resolution: {integrity: sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.20.7 + '@jridgewell/gen-mapping': 0.3.2 + jsesc: 2.5.2 + dev: true + + /@babel/helper-annotate-as-pure/7.18.6: + resolution: {integrity: sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.20.7 + dev: true + + /@babel/helper-compilation-targets/7.20.7_@babel+core@7.20.12: + resolution: {integrity: sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/compat-data': 7.20.10 + '@babel/core': 7.20.12 + '@babel/helper-validator-option': 7.18.6 + browserslist: 4.21.4 + lru-cache: 5.1.1 + semver: 6.3.0 + dev: true + + /@babel/helper-environment-visitor/7.18.9: + resolution: {integrity: sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-function-name/7.19.0: + resolution: {integrity: sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.20.7 + '@babel/types': 7.20.7 + dev: true + + /@babel/helper-hoist-variables/7.18.6: + resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.20.7 + dev: true + + /@babel/helper-module-imports/7.18.6: + resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.20.7 + dev: true + + /@babel/helper-module-transforms/7.20.11: + resolution: {integrity: sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-module-imports': 7.18.6 + '@babel/helper-simple-access': 7.20.2 + '@babel/helper-split-export-declaration': 7.18.6 + '@babel/helper-validator-identifier': 7.19.1 + '@babel/template': 7.20.7 + '@babel/traverse': 7.20.12 + '@babel/types': 7.20.7 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/helper-plugin-utils/7.20.2: + resolution: {integrity: sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-simple-access/7.20.2: + resolution: {integrity: sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.20.7 + dev: true + + /@babel/helper-split-export-declaration/7.18.6: + resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.20.7 + dev: true + + /@babel/helper-string-parser/7.19.4: + resolution: {integrity: sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==} + engines: {node: '>=6.9.0'} + dev: true + /@babel/helper-validator-identifier/7.19.1: resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==} engines: {node: '>=6.9.0'} dev: true + /@babel/helper-validator-option/7.18.6: + resolution: {integrity: sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helpers/7.20.7: + resolution: {integrity: sha512-PBPjs5BppzsGaxHQCDKnZ6Gd9s6xl8bBCluz3vEInLGRJmnZan4F6BYCeqtyXqkk4W5IlPmjK4JlOuZkpJ3xZA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.20.7 + '@babel/traverse': 7.20.12 + '@babel/types': 7.20.7 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/highlight/7.18.6: resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==} engines: {node: '>=6.9.0'} @@ -69,6 +222,81 @@ packages: js-tokens: 4.0.0 dev: true + /@babel/parser/7.20.7: + resolution: {integrity: sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.20.7 + dev: true + + /@babel/plugin-syntax-jsx/7.18.6: + resolution: {integrity: sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-react-jsx-development/7.18.6: + resolution: {integrity: sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/plugin-transform-react-jsx': 7.20.7 + dev: true + + /@babel/plugin-transform-react-jsx/7.20.7: + resolution: {integrity: sha512-Tfq7qqD+tRj3EoDhY00nn2uP2hsRxgYGi5mLQ5TimKav0a9Lrpd4deE+fcLXU8zFYRjlKPHZhpCvfEA6qnBxqQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/helper-annotate-as-pure': 7.18.6 + '@babel/helper-module-imports': 7.18.6 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-jsx': 7.18.6 + '@babel/types': 7.20.7 + dev: true + + /@babel/template/7.20.7: + resolution: {integrity: sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.18.6 + '@babel/parser': 7.20.7 + '@babel/types': 7.20.7 + dev: true + + /@babel/traverse/7.20.12: + resolution: {integrity: sha512-MsIbFN0u+raeja38qboyF8TIT7K0BFzz/Yd/77ta4MsUsmP2RAnidIlwq7d5HFQrH/OZJecGV6B71C4zAgpoSQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.18.6 + '@babel/generator': 7.20.7 + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-function-name': 7.19.0 + '@babel/helper-hoist-variables': 7.18.6 + '@babel/helper-split-export-declaration': 7.18.6 + '@babel/parser': 7.20.7 + '@babel/types': 7.20.7 + debug: 4.3.4 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/types/7.20.7: + resolution: {integrity: sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.19.4 + '@babel/helper-validator-identifier': 7.19.1 + to-fast-properties: 2.0.0 + dev: true + /@commitlint/cli/17.3.0: resolution: {integrity: sha512-/H0md7TsKflKzVPz226VfXzVafJFO1f9+r2KcFvmBu08V0T56lZU1s8WL7/xlxqLMqBTVaBf7Ixtc4bskdEEZg==} engines: {node: '>=v14'} @@ -457,11 +685,33 @@ packages: resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} dev: true + /@jridgewell/gen-mapping/0.1.1: + resolution: {integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.1.2 + '@jridgewell/sourcemap-codec': 1.4.14 + dev: true + + /@jridgewell/gen-mapping/0.3.2: + resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.1.2 + '@jridgewell/sourcemap-codec': 1.4.14 + '@jridgewell/trace-mapping': 0.3.9 + dev: true + /@jridgewell/resolve-uri/3.1.0: resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} engines: {node: '>=6.0.0'} dev: true + /@jridgewell/set-array/1.1.2: + resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} + engines: {node: '>=6.0.0'} + dev: true + /@jridgewell/sourcemap-codec/1.4.14: resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} dev: true @@ -520,6 +770,22 @@ packages: - typescript dev: true + /@pionxzh/eslint-config-react/0.1.1_lzzuuodtsqwxnvqeq4g4likcqa: + resolution: {integrity: sha512-/3ZOUZLBbxgtsGnzXSui0isMOySHjjBChE5DqhRBlrlmJhCMTx2UvMj5/HDIcnkSX9YvNwMKOdLTvjFWeRbduw==} + peerDependencies: + eslint: '>=7.4.0' + dependencies: + '@pionxzh/eslint-config-ts': 0.1.1_lzzuuodtsqwxnvqeq4g4likcqa + eslint: 8.30.0 + eslint-plugin-react: 7.32.0_eslint@8.30.0 + eslint-plugin-react-hooks: 4.6.0_eslint@8.30.0 + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + - typescript + dev: true + /@pionxzh/eslint-config-ts/0.1.1_lzzuuodtsqwxnvqeq4g4likcqa: resolution: {integrity: sha512-kMCOpNjHExpp8eo424SgTPP0V7qrVuSpw+TgrmqiYPdjVgeZpVe1RSmEghcG45TUq3cH684mpBcWE+y7q1TraQ==} peerDependencies: @@ -537,6 +803,67 @@ packages: - supports-color dev: true + /@preact/preset-vite/2.5.0_preact@10.11.3+vite@4.0.3: + resolution: {integrity: sha512-BUhfB2xQ6ex0yPkrT1Z3LbfPzjpJecOZwQ/xJrXGFSZD84+ObyS//41RdEoQCMWsM0t7UHGaujUxUBub7WM1Jw==} + peerDependencies: + '@babel/core': 7.x + vite: 2.x || 3.x || 4.x + dependencies: + '@babel/plugin-transform-react-jsx': 7.20.7 + '@babel/plugin-transform-react-jsx-development': 7.18.6 + '@prefresh/vite': 2.2.9_preact@10.11.3+vite@4.0.3 + '@rollup/pluginutils': 4.2.1 + babel-plugin-transform-hook-names: 1.0.2 + debug: 4.3.4 + kolorist: 1.6.0 + resolve: 1.22.1 + vite: 4.0.3 + transitivePeerDependencies: + - preact + - supports-color + dev: true + + /@prefresh/babel-plugin/0.4.4: + resolution: {integrity: sha512-/EvgIFMDL+nd20WNvMO0JQnzIl1EJPgmSaSYrZUww7A+aSdKsi37aL07TljrZR1cBMuzFxcr4xvqsUQLFJEukw==} + dev: true + + /@prefresh/core/1.4.1_preact@10.11.3: + resolution: {integrity: sha512-og1vaBj3LMJagVncNrDb37Gqc0cWaUcDbpVt5hZtsN4i2Iwzd/5hyTsDHvlMirhSym3wL9ihU0Xa2VhSaOue7g==} + peerDependencies: + preact: ^10.0.0 + dependencies: + preact: 10.11.3 + dev: true + + /@prefresh/utils/1.1.3: + resolution: {integrity: sha512-Mb9abhJTOV4yCfkXrMrcgFiFT7MfNOw8sDa+XyZBdq/Ai2p4Zyxqsb3EgHLOEdHpMj6J9aiZ54W8H6FTam1u+A==} + dev: true + + /@prefresh/vite/2.2.9_preact@10.11.3+vite@4.0.3: + resolution: {integrity: sha512-1ERBF85Ja9/lkrfaltmo4Gca7R2ClQPSHHDDysFgfvPzHmLUeyB0x9WHwhwov/AA1DnyPhsfYT54z3yQd8XrgA==} + peerDependencies: + preact: ^10.4.0 + vite: '>=2.0.0-beta.3' + dependencies: + '@babel/core': 7.20.12 + '@prefresh/babel-plugin': 0.4.4 + '@prefresh/core': 1.4.1_preact@10.11.3 + '@prefresh/utils': 1.1.3 + '@rollup/pluginutils': 4.2.1 + preact: 10.11.3 + vite: 4.0.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@rollup/pluginutils/4.2.1: + resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==} + engines: {node: '>= 8.0.0'} + dependencies: + estree-walker: 2.0.2 + picomatch: 2.3.1 + dev: true + /@tsconfig/node10/1.0.9: resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} dev: true @@ -615,7 +942,7 @@ packages: ignore: 5.2.0 natural-compare-lite: 1.4.0 regexpp: 3.2.0 - semver: 7.3.7 + semver: 7.3.8 tsutils: 3.21.0_typescript@4.9.4 typescript: 4.9.4 transitivePeerDependencies: @@ -689,7 +1016,7 @@ packages: debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 - semver: 7.3.7 + semver: 7.3.8 tsutils: 3.21.0_typescript@4.9.4 typescript: 4.9.4 transitivePeerDependencies: @@ -710,7 +1037,7 @@ packages: eslint: 8.30.0 eslint-scope: 5.1.1 eslint-utils: 3.0.0_eslint@8.30.0 - semver: 7.3.7 + semver: 7.3.8 transitivePeerDependencies: - supports-color - typescript @@ -838,13 +1165,13 @@ packages: resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} dev: true - /array-includes/3.1.5: - resolution: {integrity: sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==} + /array-includes/3.1.6: + resolution: {integrity: sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 define-properties: 1.1.4 - es-abstract: 1.20.3 + es-abstract: 1.21.1 get-intrinsic: 1.1.3 is-string: 1.0.7 dev: true @@ -860,10 +1187,30 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.1.4 - es-abstract: 1.20.3 + es-abstract: 1.21.1 es-shim-unscopables: 1.0.0 dev: true + /array.prototype.flatmap/1.3.1: + resolution: {integrity: sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.21.1 + es-shim-unscopables: 1.0.0 + dev: true + + /array.prototype.tosorted/1.1.1: + resolution: {integrity: sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.21.1 + es-shim-unscopables: 1.0.0 + get-intrinsic: 1.1.3 + dev: true + /arrify/1.0.1: resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} engines: {node: '>=0.10.0'} @@ -879,6 +1226,17 @@ packages: engines: {node: '>=8'} dev: true + /available-typed-arrays/1.0.5: + resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} + engines: {node: '>= 0.4'} + dev: true + + /babel-plugin-transform-hook-names/1.0.2: + resolution: {integrity: sha512-5gafyjyyBTTdX/tQQ0hRgu4AhNHG/hqWi0ZZmg2xvs2FgRkJXzDNKBZCyoYqgFkovfDrgM8OoKg8karoUvWeCw==} + peerDependencies: + '@babel/core': ^7.12.10 + dev: true + /balanced-match/1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} dev: true @@ -902,6 +1260,17 @@ packages: fill-range: 7.0.1 dev: true + /browserslist/4.21.4: + resolution: {integrity: sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001442 + electron-to-chromium: 1.4.284 + node-releases: 2.0.8 + update-browserslist-db: 1.0.10_browserslist@4.21.4 + dev: true + /builtin-modules/3.3.0: resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} engines: {node: '>=6'} @@ -954,6 +1323,10 @@ packages: engines: {node: '>=10'} dev: true + /caniuse-lite/1.0.30001442: + resolution: {integrity: sha512-239m03Pqy0hwxYPYR5JwOIxRJfLTWtle9FV8zosfV5pHg+/51uD4nxcUlM8+mWWGfwKtt8lJNHnD3cWw9VZ6ow==} + dev: true + /chalk/2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -1110,6 +1483,10 @@ packages: through2: 4.0.2 dev: true + /convert-source-map/1.9.0: + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + dev: true + /cosmiconfig-typescript-loader/4.3.0_or2qrscwofpw4jwfbychkdzroy: resolution: {integrity: sha512-NTxV1MFfZDLPiBMjxbHRwSh5LaLcPMwNdCutmnHJCKoVnlvldPWlllonKwrsRJ5pYZBIBGRWWU2tfvzxgeSW5Q==} engines: {node: '>=12', npm: '>=6'} @@ -1330,6 +1707,10 @@ packages: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} dev: true + /electron-to-chromium/1.4.284: + resolution: {integrity: sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==} + dev: true + /emoji-regex/8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} dev: true @@ -1348,34 +1729,52 @@ packages: is-arrayish: 0.2.1 dev: true - /es-abstract/1.20.3: - resolution: {integrity: sha512-AyrnaKVpMzljIdwjzrj+LxGmj8ik2LckwXacHqrJJ/jxz6dDDBcZ7I7nlHM0FvEW8MfbWJwOd+yT2XzYW49Frw==} + /es-abstract/1.21.1: + resolution: {integrity: sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg==} engines: {node: '>= 0.4'} dependencies: + available-typed-arrays: 1.0.5 call-bind: 1.0.2 + es-set-tostringtag: 2.0.1 es-to-primitive: 1.2.1 function-bind: 1.1.1 function.prototype.name: 1.1.5 get-intrinsic: 1.1.3 get-symbol-description: 1.0.0 + globalthis: 1.0.3 + gopd: 1.0.1 has: 1.0.3 has-property-descriptors: 1.0.0 + has-proto: 1.0.1 has-symbols: 1.0.3 - internal-slot: 1.0.3 + internal-slot: 1.0.4 + is-array-buffer: 3.0.1 is-callable: 1.2.7 is-negative-zero: 2.0.2 is-regex: 1.1.4 is-shared-array-buffer: 1.0.2 is-string: 1.0.7 + is-typed-array: 1.1.10 is-weakref: 1.0.2 object-inspect: 1.12.2 object-keys: 1.1.1 object.assign: 4.1.4 regexp.prototype.flags: 1.4.3 safe-regex-test: 1.0.0 - string.prototype.trimend: 1.0.5 - string.prototype.trimstart: 1.0.5 + string.prototype.trimend: 1.0.6 + string.prototype.trimstart: 1.0.6 + typed-array-length: 1.0.4 unbox-primitive: 1.0.2 + which-typed-array: 1.1.9 + dev: true + + /es-set-tostringtag/2.0.1: + resolution: {integrity: sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.1.3 + has: 1.0.3 + has-tostringtag: 1.0.0 dev: true /es-shim-unscopables/1.0.0: @@ -1519,7 +1918,7 @@ packages: optional: true dependencies: '@typescript-eslint/parser': 5.45.0_lzzuuodtsqwxnvqeq4g4likcqa - array-includes: 3.1.5 + array-includes: 3.1.6 array.prototype.flat: 1.3.0 debug: 2.6.9 doctrine: 2.1.0 @@ -1527,10 +1926,10 @@ packages: eslint-import-resolver-node: 0.3.6 eslint-module-utils: 2.7.4_4edlp5lk3fcbznkdi6lylxkfvu has: 1.0.3 - is-core-module: 2.10.0 + is-core-module: 2.11.0 is-glob: 4.0.3 minimatch: 3.1.2 - object.values: 1.1.5 + object.values: 1.1.6 resolve: 1.22.1 tsconfig-paths: 3.14.1 transitivePeerDependencies: @@ -1599,6 +1998,39 @@ packages: eslint: 8.30.0 dev: true + /eslint-plugin-react-hooks/4.6.0_eslint@8.30.0: + resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + dependencies: + eslint: 8.30.0 + dev: true + + /eslint-plugin-react/7.32.0_eslint@8.30.0: + resolution: {integrity: sha512-vSBi1+SrPiLZCGvxpiZIa28fMEUaMjXtCplrvxcIxGzmFiYdsXQDwInEjuv5/i/2CTTxbkS87tE8lsQ0Qxinbw==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + dependencies: + array-includes: 3.1.6 + array.prototype.flatmap: 1.3.1 + array.prototype.tosorted: 1.1.1 + doctrine: 2.1.0 + eslint: 8.30.0 + estraverse: 5.3.0 + jsx-ast-utils: 3.3.3 + minimatch: 3.1.2 + object.entries: 1.1.6 + object.fromentries: 2.0.6 + object.hasown: 1.1.2 + object.values: 1.1.6 + prop-types: 15.8.1 + resolve: 2.0.0-next.4 + semver: 6.3.0 + string.prototype.matchall: 4.0.8 + dev: true + /eslint-plugin-unicorn/44.0.2_eslint@8.30.0: resolution: {integrity: sha512-GLIDX1wmeEqpGaKcnMcqRvMVsoabeF0Ton0EX4Th5u6Kmf7RM9WBl705AXFEsns56ESkEs0uyelLuUTvz9Tr0w==} engines: {node: '>=14.18'} @@ -1618,7 +2050,7 @@ packages: read-pkg-up: 7.0.1 regexp-tree: 0.1.24 safe-regex: 2.1.1 - semver: 7.3.7 + semver: 7.3.8 strip-indent: 3.0.0 dev: true @@ -1766,6 +2198,10 @@ packages: engines: {node: '>=4.0'} dev: true + /estree-walker/2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + dev: true + /esutils/2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} @@ -1872,6 +2308,12 @@ packages: resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} dev: true + /for-each/0.3.3: + resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + dependencies: + is-callable: 1.2.7 + dev: true + /fs-extra/10.1.0: resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} engines: {node: '>=12'} @@ -1901,7 +2343,7 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.1.4 - es-abstract: 1.20.3 + es-abstract: 1.21.1 functions-have-names: 1.2.3 dev: true @@ -1909,6 +2351,11 @@ packages: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} dev: true + /gensync/1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + dev: true + /get-caller-file/2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} @@ -1979,6 +2426,11 @@ packages: ini: 1.3.8 dev: true + /globals/11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + dev: true + /globals/13.19.0: resolution: {integrity: sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==} engines: {node: '>=8'} @@ -1986,6 +2438,13 @@ packages: type-fest: 0.20.2 dev: true + /globalthis/1.0.3: + resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} + engines: {node: '>= 0.4'} + dependencies: + define-properties: 1.1.4 + dev: true + /globby/11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} @@ -2009,6 +2468,12 @@ packages: slash: 4.0.0 dev: true + /gopd/1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + dependencies: + get-intrinsic: 1.1.3 + dev: true + /graceful-fs/4.2.10: resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} dev: true @@ -2042,6 +2507,11 @@ packages: get-intrinsic: 1.1.3 dev: true + /has-proto/1.0.1: + resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} + engines: {node: '>= 0.4'} + dev: true + /has-symbols/1.0.3: resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} engines: {node: '>= 0.4'} @@ -2155,6 +2625,15 @@ packages: side-channel: 1.0.4 dev: true + /internal-slot/1.0.4: + resolution: {integrity: sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.1.3 + has: 1.0.3 + side-channel: 1.0.4 + dev: true + /is-alphabetical/1.0.4: resolution: {integrity: sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==} dev: true @@ -2166,6 +2645,14 @@ packages: is-decimal: 1.0.4 dev: true + /is-array-buffer/3.0.1: + resolution: {integrity: sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.1.3 + is-typed-array: 1.1.10 + dev: true + /is-arrayish/0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} dev: true @@ -2196,12 +2683,6 @@ packages: engines: {node: '>= 0.4'} dev: true - /is-core-module/2.10.0: - resolution: {integrity: sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==} - dependencies: - has: 1.0.3 - dev: true - /is-core-module/2.11.0: resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==} dependencies: @@ -2327,6 +2808,17 @@ packages: text-extensions: 1.9.0 dev: true + /is-typed-array/1.1.10: + resolution: {integrity: sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + for-each: 0.3.3 + gopd: 1.0.1 + has-tostringtag: 1.0.0 + dev: true + /is-weakref/1.0.2: resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} dependencies: @@ -2358,6 +2850,12 @@ packages: argparse: 2.0.1 dev: true + /jsesc/2.5.2: + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} + hasBin: true + dev: true + /json-parse-even-better-errors/2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} dev: true @@ -2381,6 +2879,12 @@ packages: minimist: 1.2.6 dev: true + /json5/2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + dev: true + /jsonc-eslint-parser/2.1.0: resolution: {integrity: sha512-qCRJWlbP2v6HbmKW7R3lFbeiVWHo+oMJ0j+MizwvauqnCV/EvtAeEeuCgoc/ErtsuoKgYB8U4Ih8AxJbXoE6/g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -2388,7 +2892,7 @@ packages: acorn: 8.8.0 eslint-visitor-keys: 3.3.0 espree: 9.4.0 - semver: 7.3.7 + semver: 7.3.8 dev: true /jsonfile/6.1.0: @@ -2404,6 +2908,14 @@ packages: engines: {'0': node >= 0.2.0} dev: true + /jsx-ast-utils/3.3.3: + resolution: {integrity: sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==} + engines: {node: '>=4.0'} + dependencies: + array-includes: 3.1.6 + object.assign: 4.1.4 + dev: true + /junk/4.0.0: resolution: {integrity: sha512-ojtSU++zLJ3jQG9bAYjg94w+/DOJtRyD7nPaerMFrBhmdVmiV5/exYH5t4uHga4G/95nT6hr1OJoKIFbYbrW5w==} engines: {node: '>=12.20'} @@ -2414,6 +2926,10 @@ packages: engines: {node: '>=0.10.0'} dev: true + /kolorist/1.6.0: + resolution: {integrity: sha512-dLkz37Ab97HWMx9KTes3Tbi3D1ln9fCAy2zr2YVExJasDRPGRaKcoE4fycWNtnCAJfjFqe0cnY+f8KT2JePEXQ==} + dev: true + /levn/0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} @@ -2541,6 +3057,19 @@ packages: wrap-ansi: 6.2.0 dev: true + /loose-envify/1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + dependencies: + js-tokens: 4.0.0 + dev: true + + /lru-cache/5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + dependencies: + yallist: 3.1.1 + dev: true + /lru-cache/6.0.0: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} @@ -2713,6 +3242,10 @@ packages: resolution: {integrity: sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw==} dev: true + /node-releases/2.0.8: + resolution: {integrity: sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==} + dev: true + /normalize-package-data/2.5.0: resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} dependencies: @@ -2751,6 +3284,11 @@ packages: path-key: 4.0.0 dev: true + /object-assign/4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + dev: true + /object-inspect/1.12.2: resolution: {integrity: sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==} dev: true @@ -2770,13 +3308,38 @@ packages: object-keys: 1.1.1 dev: true - /object.values/1.1.5: - resolution: {integrity: sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==} + /object.entries/1.1.6: + resolution: {integrity: sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.21.1 + dev: true + + /object.fromentries/2.0.6: + resolution: {integrity: sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.21.1 + dev: true + + /object.hasown/1.1.2: + resolution: {integrity: sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==} + dependencies: + define-properties: 1.1.4 + es-abstract: 1.21.1 + dev: true + + /object.values/1.1.6: + resolution: {integrity: sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 define-properties: 1.1.4 - es-abstract: 1.20.3 + es-abstract: 1.21.1 dev: true /once/1.4.0: @@ -2975,11 +3538,22 @@ packages: picocolors: 1.0.0 source-map-js: 1.0.2 + /preact/10.11.3: + resolution: {integrity: sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==} + /prelude-ls/1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} dev: true + /prop-types/15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + dev: true + /punycode/2.1.1: resolution: {integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==} engines: {node: '>=6'} @@ -3004,6 +3578,10 @@ packages: engines: {node: '>=10'} dev: true + /react-is/16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + dev: true + /read-pkg-up/7.0.1: resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} engines: {node: '>=8'} @@ -3121,6 +3699,15 @@ packages: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + /resolve/2.0.0-next.4: + resolution: {integrity: sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==} + hasBin: true + dependencies: + is-core-module: 2.11.0 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + /restore-cursor/3.1.0: resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} engines: {node: '>=8'} @@ -3326,20 +3913,33 @@ packages: strip-ansi: 7.0.1 dev: true - /string.prototype.trimend/1.0.5: - resolution: {integrity: sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==} + /string.prototype.matchall/4.0.8: + resolution: {integrity: sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.21.1 + get-intrinsic: 1.1.3 + has-symbols: 1.0.3 + internal-slot: 1.0.3 + regexp.prototype.flags: 1.4.3 + side-channel: 1.0.4 + dev: true + + /string.prototype.trimend/1.0.6: + resolution: {integrity: sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==} dependencies: call-bind: 1.0.2 define-properties: 1.1.4 - es-abstract: 1.20.3 + es-abstract: 1.21.1 dev: true - /string.prototype.trimstart/1.0.5: - resolution: {integrity: sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==} + /string.prototype.trimstart/1.0.6: + resolution: {integrity: sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==} dependencies: call-bind: 1.0.2 define-properties: 1.1.4 - es-abstract: 1.20.3 + es-abstract: 1.21.1 dev: true /string_decoder/1.3.0: @@ -3439,6 +4039,11 @@ packages: readable-stream: 3.6.0 dev: true + /to-fast-properties/2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + dev: true + /to-regex-range/5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -3612,6 +4217,14 @@ packages: engines: {node: '>=10'} dev: true + /typed-array-length/1.0.4: + resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} + dependencies: + call-bind: 1.0.2 + for-each: 0.3.3 + is-typed-array: 1.1.10 + dev: true + /typescript/4.9.4: resolution: {integrity: sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==} engines: {node: '>=4.2.0'} @@ -3638,6 +4251,17 @@ packages: engines: {node: '>= 10.0.0'} dev: true + /update-browserslist-db/1.0.10_browserslist@4.21.4: + resolution: {integrity: sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.21.4 + escalade: 3.1.1 + picocolors: 1.0.0 + dev: true + /uri-js/4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} dependencies: @@ -3724,6 +4348,18 @@ packages: is-symbol: 1.0.4 dev: true + /which-typed-array/1.1.9: + resolution: {integrity: sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + for-each: 0.3.3 + gopd: 1.0.1 + has-tostringtag: 1.0.0 + is-typed-array: 1.1.10 + dev: true + /which/2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -3763,6 +4399,10 @@ packages: engines: {node: '>=10'} dev: true + /yallist/3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + dev: true + /yallist/4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} dev: true