diff --git a/docs/cloud/manage/dashboards.md b/docs/cloud/manage/dashboards.md index ff2783f260c..8c38059d81e 100644 --- a/docs/cloud/manage/dashboards.md +++ b/docs/cloud/manage/dashboards.md @@ -2,7 +2,7 @@ sidebar_label: 'Dashboards' slug: /cloud/manage/dashboards title: 'Dashboards' -description: 'The SQL Console's dashboards feature allows you to collect and share visualizations from saved queries.' +description: 'The SQL Console dashboards feature allows you to collect and share visualizations from saved queries.' --- import BetaBadge from '@theme/badges/BetaBadge'; diff --git a/docs/managing-data/core-concepts/partitions.md b/docs/managing-data/core-concepts/partitions.md index fc07dc6d34a..38dc6cdb91c 100644 --- a/docs/managing-data/core-concepts/partitions.md +++ b/docs/managing-data/core-concepts/partitions.md @@ -24,7 +24,6 @@ Partitioning can be enabled when a table is initially defined via the [PARTITION To illustrate this, we [enhance](https://sql.clickhouse.com/?query=U0hPVyBDUkVBVEUgVEFCTEUgdWsudWtfcHJpY2VfcGFpZF9zaW1wbGVfcGFydGl0aW9uZWQ&run_query=true&tab=results) the [What are table parts](/parts) example table by adding a `PARTITION BY toStartOfMonth(date)` clause, which organizes the table`s data parts based on the months of property sales: - ```sql CREATE TABLE uk.uk_price_paid_simple_partitioned ( @@ -67,26 +66,15 @@ As sketched in the diagram above, parts belonging to different partitions are ne You can [query](https://sql.clickhouse.com/?query=U0VMRUNUIERJU1RJTkNUIF9wYXJ0aXRpb25fdmFsdWUgQVMgcGFydGl0aW9uCkZST00gdWsudWtfcHJpY2VfcGFpZF9zaW1wbGVfcGFydGl0aW9uZWQKT1JERVIgQlkgcGFydGl0aW9uIEFTQw&run_query=true&tab=results) the list of all existing unique partitions of our example table by using the [virtual column](/engines/table-engines#table_engines-virtual_columns) `_partition_value`: -```sql +```sql runnable SELECT DISTINCT _partition_value AS partition FROM uk.uk_price_paid_simple_partitioned ORDER BY partition ASC; - - - ┌─partition──────┐ - 1. │ ('1995-01-01') │ - 2. │ ('1995-02-01') │ - 3. │ ('1995-03-01') │ - ... -304. │ ('2021-04-01') │ -305. │ ('2021-05-01') │ -306. │ ('2021-06-01') │ - └────────────────┘ ``` Alternatively, ClickHouse tracks all parts and partitions of all tables in the [system.parts](/operations/system-tables/parts) system table, and the following query [returns](https://sql.clickhouse.com/?query=U0VMRUNUCiAgICBwYXJ0aXRpb24sCiAgICBjb3VudCgpIEFTIHBhcnRzLAogICAgc3VtKHJvd3MpIEFTIHJvd3MKRlJPTSBzeXN0ZW0ucGFydHMKV0hFUkUgKGRhdGFiYXNlID0gJ3VrJykgQU5EIChgdGFibGVgID0gJ3VrX3ByaWNlX3BhaWRfc2ltcGxlX3BhcnRpdGlvbmVkJykgQU5EIGFjdGl2ZQpHUk9VUCBCWSBwYXJ0aXRpb24KT1JERVIgQlkgcGFydGl0aW9uIEFTQzs&run_query=true&tab=results) for our example table above the list of all partitions, plus the current number of active parts and the sum of rows in these parts per partition: -```sql +```sql runnable SELECT partition, count() AS parts, @@ -95,17 +83,6 @@ FROM system.parts WHERE (database = 'uk') AND (`table` = 'uk_price_paid_simple_partitioned') AND active GROUP BY partition ORDER BY partition ASC; - - - ┌─partition──┬─parts─┬───rows─┐ - 1. │ 1995-01-01 │ 1 │ 50473 │ - 2. │ 1995-02-01 │ 1 │ 50840 │ - 3. │ 1995-03-01 │ 1 │ 71276 │ - ... -304. │ 2021-04-01 │ 3 │ 23160 │ -305. │ 2021-05-01 │ 3 │ 17607 │ -306. │ 2021-06-01 │ 3 │ 5652 │ - └─partition──┴─parts─┴───rows─┘ ``` @@ -152,20 +129,12 @@ TTL date + INTERVAL 12 MONTH TO VOLUME 'slow_but_cheap'; Partitions can assist with query performance, but this depends heavily on the access patterns. If queries target only a few partitions (ideally one), performance can potentially improve. This is only typically useful if the partitioning key is not in the primary key and you are filtering by it, as shown in the example query below. -```sql +```sql runnable SELECT MAX(price) AS highest_price -FROM uk_price_paid_simple_partitioned +FROM uk.uk_price_paid_simple_partitioned WHERE date >= '2020-12-01' AND date <= '2020-12-31' AND town = 'LONDON'; - - - ┌─highest_price─┐ -1. │ 296280000 │ -- 296.28 million - └───────────────┘ - -1 row in set. Elapsed: 0.006 sec. Processed 8.19 thousand rows, 57.34 KB (1.36 million rows/s., 9.49 MB/s.) -Peak memory usage: 2.73 MiB. ``` The query runs over our example table from above and [calculates](https://sql.clickhouse.com/?query=U0VMRUNUIE1BWChwcmljZSkgQVMgaGlnaGVzdF9wcmljZQpGUk9NIHVrLnVrX3ByaWNlX3BhaWRfc2ltcGxlX3BhcnRpdGlvbmVkCldIRVJFIGRhdGUgPj0gJzIwMjAtMTItMDEnCiAgQU5EIGRhdGUgPD0gJzIwMjAtMTItMzEnCiAgQU5EIHRvd24gPSAnTE9ORE9OJzs&run_query=true&tab=results) the highest price of all sold properties in London in December 2020 by filtering on both a column (`date`) used in the table's partition key and on a column (`town`) used in the table's primary key (and `date` is not part of the primary key). @@ -182,10 +151,10 @@ ClickHouse processes that query by applying a sequence of pruning techniques to We can observe these data pruning steps by [inspecting](https://sql.clickhouse.com/?query=RVhQTEFJTiBpbmRleGVzID0gMQpTRUxFQ1QgTUFYKHByaWNlKSBBUyBoaWdoZXN0X3ByaWNlCkZST00gdWsudWtfcHJpY2VfcGFpZF9zaW1wbGVfcGFydGl0aW9uZWQKV0hFUkUgZGF0ZSA-PSAnMjAyMC0xMi0wMScKICBBTkQgZGF0ZSA8PSAnMjAyMC0xMi0zMScKICBBTkQgdG93biA9ICdMT05ET04nOw&run_query=true&tab=results) the physical query execution plan for our example query from above via an [EXPLAIN](/sql-reference/statements/explain) clause : -```sql +```sql style="fontSize:13px" EXPLAIN indexes = 1 SELECT MAX(price) AS highest_price -FROM uk_price_paid_simple_partitioned +FROM uk.uk_price_paid_simple_partitioned WHERE date >= '2020-12-01' AND date <= '2020-12-31' AND town = 'LONDON'; @@ -240,23 +209,18 @@ With partitioning, the data is usually distributed across more data parts, which We can demonstrate this by running the same query over both the [What are table parts](/parts) example table (without partitioning enabled), and our current example table from above (with partitioning enabled). Both tables [contain](https://sql.clickhouse.com/?query=U0VMRUNUCiAgICB0YWJsZSwKICAgIHN1bShyb3dzKSBBUyByb3dzCkZST00gc3lzdGVtLnBhcnRzCldIRVJFIChkYXRhYmFzZSA9ICd1aycpIEFORCAoYHRhYmxlYCBJTiBbJ3VrX3ByaWNlX3BhaWRfc2ltcGxlJywgJ3VrX3ByaWNlX3BhaWRfc2ltcGxlX3BhcnRpdGlvbmVkJ10pIEFORCBhY3RpdmUKR1JPVVAgQlkgdGFibGU7&run_query=true&tab=results) the same data and number of rows: -```sql +```sql runnable SELECT table, sum(rows) AS rows FROM system.parts WHERE (database = 'uk') AND (table IN ['uk_price_paid_simple', 'uk_price_paid_simple_partitioned']) AND active GROUP BY table; - - ┌─table────────────────────────────┬─────rows─┐ -1. │ uk_price_paid_simple │ 25248433 │ -2. │ uk_price_paid_simple_partitioned │ 25248433 │ - └──────────────────────────────────┴──────────┘ ``` However, the table with partitions enabled, [has](https://sql.clickhouse.com/?query=U0VMRUNUCiAgICB0YWJsZSwKICAgIGNvdW50KCkgQVMgcGFydHMKRlJPTSBzeXN0ZW0ucGFydHMKV0hFUkUgKGRhdGFiYXNlID0gJ3VrJykgQU5EIChgdGFibGVgIElOIFsndWtfcHJpY2VfcGFpZF9zaW1wbGUnLCAndWtfcHJpY2VfcGFpZF9zaW1wbGVfcGFydGl0aW9uZWQnXSkgQU5EIGFjdGl2ZQpHUk9VUCBCWSB0YWJsZTs&run_query=true&tab=results) more active [data parts](/parts), because, as mentioned above, ClickHouse only [merges](/parts) data parts within, but not across partitions: -```sql +```sql runnable SELECT table, count() AS parts @@ -264,13 +228,8 @@ FROM system.parts WHERE (database = 'uk') AND (table IN ['uk_price_paid_simple', 'uk_price_paid_simple_partitioned']) AND active GROUP BY table; - - ┌─table────────────────────────────┬─parts─┐ -1. │ uk_price_paid_simple │ 1 │ -2. │ uk_price_paid_simple_partitioned │ 436 │ - └──────────────────────────────────┴───────┘ ``` -As shown further above, the partitioned table `uk_price_paid_simple_partitioned` has 306 partitions, and therefore at least 306 active data parts. Whereas for our non-partitioned table `uk_price_paid_simple` all [initial](/parts) data parts could be merged into a single active part by background merges. +As shown further above, the partitioned table `uk_price_paid_simple_partitioned` has over 600 partitions, and therefore at 600 306 active data parts. Whereas for our non-partitioned table `uk_price_paid_simple` all [initial](/parts) data parts could be merged into a single active part by background merges. When we [check](https://sql.clickhouse.com/?query=RVhQTEFJTiBpbmRleGVzID0gMQpTRUxFQ1QgTUFYKHByaWNlKSBBUyBoaWdoZXN0X3ByaWNlCkZST00gdWsudWtfcHJpY2VfcGFpZF9zaW1wbGVfcGFydGl0aW9uZWQKV0hFUkUgdG93biA9ICdMT05ET04nOw&run_query=true&tab=results) the physical query execution plan with an [EXPLAIN](/sql-reference/statements/explain) clause for our example query from above without the partition filter running over the partitioned table, we can see in row 19 and 20 of the output below that ClickHouse identified 671 out of 3257 existing [granules](/guides/best-practices/sparse-primary-indexes#data-is-organized-into-granules-for-parallel-data-processing) (blocks of rows) spread over 431 out of 436 existing active data parts that potentially contain rows matching the query's filter, and therefore will be scanned and processed by the query engine: @@ -338,10 +297,9 @@ SELECT MAX(price) AS highest_price FROM uk.uk_price_paid_simple_partitioned WHERE town = 'LONDON'; - - ┌─highest_price─┐ -1. │ 594300000 │ -- 594.30 million - └───────────────┘ +┌─highest_price─┐ +│ 594300000 │ -- 594.30 million +└───────────────┘ 1 row in set. Elapsed: 0.090 sec. Processed 5.48 million rows, 27.95 MB (60.66 million rows/s., 309.51 MB/s.) Peak memory usage: 163.44 MiB. @@ -355,10 +313,9 @@ SELECT MAX(price) AS highest_price FROM uk.uk_price_paid_simple WHERE town = 'LONDON'; - - ┌─highest_price─┐ -1. │ 594300000 │ -- 594.30 million - └───────────────┘ +┌─highest_price─┐ +│ 594300000 │ -- 594.30 million +└───────────────┘ 1 row in set. Elapsed: 0.012 sec. Processed 1.97 million rows, 9.87 MB (162.23 million rows/s., 811.17 MB/s.) Peak memory usage: 62.02 MiB. diff --git a/docs/quick-start.mdx b/docs/quick-start.mdx index e78b8082f0f..f5da552e1db 100644 --- a/docs/quick-start.mdx +++ b/docs/quick-start.mdx @@ -138,7 +138,7 @@ ORDER BY timestamp ``` Notice the response comes back in a nice table format: -```response +```text ┌─user_id─┬─message────────────────────────────────────────────┬───────────timestamp─┬──metric─┐ │ 102 │ Insert a lot of rows per batch │ 2022-03-21 00:00:00 │ 1.41421 │ │ 102 │ Sort your data based on your commonly-used queries │ 2022-03-22 00:00:00 │ 2.718 │ diff --git a/docusaurus.config.en.js b/docusaurus.config.en.js index 74485aedd4f..cb99578f867 100644 --- a/docusaurus.config.en.js +++ b/docusaurus.config.en.js @@ -287,7 +287,7 @@ const config = { prism: { theme: themes.darkTheme, darkTheme: themes.darkTheme, - additionalLanguages: ["java", "cpp", "rust"], + additionalLanguages: ["java", "cpp", "rust", "python", "javascript"], magicComments: [ // Remember to extend the default highlight class name as well! { @@ -355,7 +355,8 @@ const config = { [ pluginLlmsTxt, {} - ] + ], + ['./plugins/tailwind-config.js', {}], ], customFields: { blogSidebarLink: "/docs/knowledgebase", // Used for KB article page diff --git a/docusaurus.config.jp.js b/docusaurus.config.jp.js index ad9ba589602..6659d1cc8af 100644 --- a/docusaurus.config.jp.js +++ b/docusaurus.config.jp.js @@ -254,7 +254,7 @@ const config = { prism: { theme: themes.darkTheme, darkTheme: themes.darkTheme, - additionalLanguages: ["java", "cpp", "rust"], + additionalLanguages: ["java", "cpp", "rust", "python", "javascript"], magicComments: [ // Remember to extend the default highlight class name as well! { @@ -292,6 +292,7 @@ const config = { }, ], chHeader, + ['./plugins/tailwind-config.js', {}], ], customFields: { blogSidebarLink: "/docs/knowledgebase", // Used for KB article page diff --git a/docusaurus.config.ru.js b/docusaurus.config.ru.js index 04dafb79646..296dd6ae3cd 100644 --- a/docusaurus.config.ru.js +++ b/docusaurus.config.ru.js @@ -265,7 +265,7 @@ const config = { prism: { theme: themes.darkTheme, darkTheme: themes.darkTheme, - additionalLanguages: ["java", "cpp", "rust"], + additionalLanguages: ["java", "cpp", "rust", "python", "javascript"], magicComments: [ // Remember to extend the default highlight class name as well! { @@ -309,6 +309,7 @@ const config = { }, ], chHeader, + ['./plugins/tailwind-config.js', {}], ], customFields: { blogSidebarLink: "/docs/knowledgebase", // Used for KB article page diff --git a/docusaurus.config.zh.js b/docusaurus.config.zh.js index 7c83ababc9e..6ffc38e7a45 100644 --- a/docusaurus.config.zh.js +++ b/docusaurus.config.zh.js @@ -254,7 +254,7 @@ const config = { prism: { theme: themes.darkTheme, darkTheme: themes.darkTheme, - additionalLanguages: ["java", "cpp", "rust"], + additionalLanguages: ["java", "cpp", "rust", "python", "javascript"], magicComments: [ // Remember to extend the default highlight class name as well! { @@ -292,6 +292,7 @@ const config = { }, ], chHeader, + ['./plugins/tailwind-config.js', {}], ], customFields: { blogSidebarLink: "/docs/knowledgebase", // Used for KB article page diff --git a/package.json b/package.json index b76d7ee167b..55b777f839c 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,8 @@ "run-markdown-linter": "yarn markdownlint-cli2 --config ./scripts/.markdownlint-cli2.yaml 'docs/**/*.md'" }, "dependencies": { + "@clickhouse/click-ui": "^0.0.199", + "@clickhouse/client-web": "^1.11.0", "@docusaurus/core": "3.7.0", "@docusaurus/faster": "^3.7.0", "@docusaurus/plugin-client-redirects": "^3.7.0", @@ -41,8 +43,11 @@ "axios": "^1.8.2", "clsx": "^2.1.0", "docusaurus-plugin-sass": "^0.2.6", + "echarts": "^5.6.0", + "echarts-for-react": "^3.0.2", "esbuild": "^0.25.0", "esbuild-loader": "^4.0.3", + "filesize": "^10.1.6", "flexsearch": "^0.7.43", "gray-matter": "^4.0.3", "hast-util-is-element": "1.1.0", @@ -50,6 +55,7 @@ "katex": "^0.16.21", "markdownlint-cli2": "^0.17.2", "node-fetch": "^3.3.2", + "numeral": "^2.0.6", "prism-react-renderer": "^2.4.1", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -59,18 +65,23 @@ "remark-link-rewrite": "^1.0.7", "remark-math": "^6.0.0", "sass": "^1.86.1", - "search-insights": "^2.17.3" + "search-insights": "^2.17.3", + "short-uuid": "^5.2.0" }, "devDependencies": { "@argos-ci/cli": "^2.5.5", "@argos-ci/playwright": "^3.9.4", "@docusaurus/module-type-aliases": "3.7.0", "@playwright/test": "^1.51.1", + "@tailwindcss/postcss": "^4.1.3", + "@tailwindcss/typography": "^0.5.16", "@types/react": "^19.0.4", "@types/styled-jsx": "^3.4.4", "cheerio": "^1.0.0", "markdownlint-rule-helpers": "^0.28.0", + "postcss": "^8.5.3", "rsync": "^0.6.1", + "tailwindcss": "^4.1.3", "typescript": "^5.8.2" }, "browserslist": { diff --git a/plugins/tailwind-config.js b/plugins/tailwind-config.js new file mode 100644 index 00000000000..f2ded6a68fb --- /dev/null +++ b/plugins/tailwind-config.js @@ -0,0 +1,9 @@ +module.exports = function tailwindPlugin(context, options) { + return { + name: 'tailwind-plugin', + configurePostCss(postcssOptions) { + postcssOptions.plugins = [require('@tailwindcss/postcss')] + return postcssOptions + } + } +} \ No newline at end of file diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 00000000000..241bd693e33 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,5 @@ +module.exports = { + plugins: { + '@tailwindcss/postcss': {}, + } +} \ No newline at end of file diff --git a/src/components/CodeViewer/CodeInterpreter.tsx b/src/components/CodeViewer/CodeInterpreter.tsx new file mode 100644 index 00000000000..c76e7fe55a9 --- /dev/null +++ b/src/components/CodeViewer/CodeInterpreter.tsx @@ -0,0 +1,296 @@ +import { Button, Icon, RadioGroup, Tooltip } from '@clickhouse/click-ui/bundled' +import { createClient as createWebClient } from '@clickhouse/client-web' +import { parse } from 'json5' +import { useEffect, useState } from 'react' +import short from 'short-uuid' +import CodeResults, { DefaultView } from './CodeResults' +import { + ChartConfig, + ChartType, + QueryParameter, + QueryResponse, + QueryResults +} from './types' +import { formatBytes, formatReadableRows, roundToDynamicPrecision } from './utils' +import { getGoogleAnalyticsUserIdFromBrowserCookie } from '../../lib/google/google' + + +interface Props { + queryString: string + runnable: boolean + link: string + run: boolean + view: DefaultView + chart?: { type: ChartType; config?: ChartConfig } + settings: string + show_statistics: boolean +} + +function CodeInterpreter({ + queryString, + runnable, + link, + run, + view, + chart, + settings, + show_statistics +}: Props) { + const [results, setResults] = useState(null) + const [showResultsPanel, setShowResultsPanel] = useState(false) + const [queryRunning, setQueryRunning] = useState(false) + const [currentView, setCurrentView] = useState(view) + const [runByUser, setRunByUser] = useState(false) + + const clickhouse_settings = JSON.parse(settings) + const clickhouse_web = createWebClient({ + url: 'https://sql-clickhouse.clickhouse.com', + username: 'demo', + password: '', + clickhouse_settings: { + ...clickhouse_settings, + allow_experimental_analyzer: 1, + result_overflow_mode: 'break', + read_overflow_mode: 'break' + } + }) + + function generateId(): string { + return short.generate().toUpperCase().slice(0, 27) + } + + async function query( + query: string, + query_id: string, + params: Array, + runManually: boolean + ): Promise { + if (!query) { + return { error: 'Query not provided', status: 400, query_id: query_id } + } + query = query.replace(/;$/, '').trim() + + const query_params: { [key: string]: string } = {} + params.forEach((param) => { + if (param.type && /^(Array|Map|Tuple|Nested)/.test(param.type)) { + try { + query_params[param.name] = parse(param.value) + } catch (e) { + // just send and let clickhouse error + query_params[param.name] = param.value + } + } else { + query_params[param.name] = param.value + } + }) + + try { + // Inject metadata as log comment + const currentPath = typeof window !== 'undefined' ? window.location.pathname : '' + let jsonLogComment: Record = {} + if (typeof window !== "undefined") { + let gaId = getGoogleAnalyticsUserIdFromBrowserCookie('_ga') + if (gaId) { + jsonLogComment['ga_id'] = gaId + } + } + if (currentPath) { + jsonLogComment['url_path'] = currentPath + } + jsonLogComment['auto_run'] = !runManually + + const res = await clickhouse_web.query({ + query: query, + query_id: query_id, + query_params: query_params, + clickhouse_settings: { + log_comment: JSON.stringify(jsonLogComment), + } + }) + const json = (await res.json()) as QueryResults + if (json.exception) { + console.error('Error while running query', json.exception) + return { + query: query, + status: 500, + response: json, + query_id: query_id, + error: json.exception + } + } + return { query: query, status: 200, response: json, query_id: query_id } + } catch (error) { + console.error('Error while running query', error) + return { error: error, status: 500, query_id: query_id } + } + } + + useEffect(() => { + if (run) { + handleRunQuery(false) + } + }, [run]) + + const handleRunQuery = async (runManually: boolean) => { + + const query_run_id = generateId() + setResults({}) + setQueryRunning(true) + setShowResultsPanel(true) + + const res = await query(queryString, query_run_id, [], runManually) + setQueryRunning(false) + setResults({ + response: res.response, + query_id: res.query_id, + error: res.error + }) + setRunByUser(runManually) + } + + const closeResultPanel = (event: any) => { + event.preventDefault() + setShowResultsPanel(false) + } + + const openTableResultPanel = (event: any) => { + event.preventDefault() + setShowResultsPanel(true) + } + + const runBy = () => { + if (runByUser) { + return 'Executed by user.' + } else { + if (run) { + return 'Executed on load.' + } + } + } + + const hideTableResultButton = () => { + if (results) { + const show_results = showResultsPanel ? ( + + + + + Close the results + + ) : ( + + + + + Open the results + + ) + + return ( +
+ {show_results} + {chart && ( +
+ + { + setCurrentView(DefaultView.Table) + }} + value={DefaultView.Table} + /> + { + setCurrentView(DefaultView.Chart) + }} + value={DefaultView.Chart} + /> + +
+ )} + +
+ ) + } + } + + const runButton = () => { + if (runnable) { + return ( +
+
+
{hideTableResultButton()}
+
+ {show_statistics && results?.response?.statistics && ( +
+ {`${runBy()} Read ${formatReadableRows(results.response.statistics.rows_read)} rows and ${formatBytes(results.response.statistics.bytes_read)} in ${roundToDynamicPrecision(results.response.statistics.elapsed)} seconds`} +
+ )} +
+
+ +
+
+ + + + + Run the query + +
+ {link && ( + + + + + + + Open in Play + + )} +
+
+ ) + } + } + + return ( + <> + {runButton()} +
+ {showResultsPanel && ( + + )} +
+ + ) +} + +export default CodeInterpreter diff --git a/src/components/CodeViewer/CodeResults.tsx b/src/components/CodeViewer/CodeResults.tsx new file mode 100644 index 00000000000..9765c04666f --- /dev/null +++ b/src/components/CodeViewer/CodeResults.tsx @@ -0,0 +1,363 @@ +import { + CellProps, + createToast, + Grid, + Icon, + SelectedRegion, + SelectionFocus, + Tooltip, +} from '@clickhouse/click-ui/bundled' + +import { useCallback, useRef, useMemo, useState } from 'react' +import Chart from './charts' +import copyGridElements from './copy/copyGridElements' +import { ChartConfig, ChartType, QueryResults } from './types' +import LoadingSVG from '@site/static/images/loading.svg' +import { useColorMode } from '@docusaurus/theme-common' + +export enum DefaultView { + Chart = 'chart', + Table = 'table' +} + +interface ResultsProps { + results?: { response?: QueryResults; query_id?: string; error: string } + queryRunning: boolean + view: DefaultView + chart?: { type: ChartType; config?: ChartConfig } +} + +export enum Position { + Start = 'start', + End = 'end', + Center = 'center' +} + +interface LoadingProps { + position?: Position + className?: string +} + +const Loading: React.FC = ({ + position = Position.Start, + className = '' +}) => { + const { colorMode } = useColorMode() + const strokeColor = colorMode === 'light' ? '#000000' : '#FAFF69' + + return ( +
+
+ +   Loading +
+
+ ) +} + +const rowStart = 1 +export const PIXEL_PER_CHAR = 7 + +export function getValueOfCell( + value: string | null, + isFocused: boolean, + maxPx: number +): string | null { + if (value === null) { + return null + } + + if (isFocused) { + return value + } + + // lets assume 7px per character + const maxStrWidth = maxPx / PIXEL_PER_CHAR + + if (value.length <= maxStrWidth) { + return value + } + + return value.substring(0, maxStrWidth) + '...' +} + +interface SelectedCell { + row: number, + column: number +} + +function CodeResults(props: ResultsProps) { + const [selectedCell, setSelectedCell] = useState({ row: 1, column: 0 }); + const response = props.results?.response + const error = props.results?.error + const gridRef = useRef(null) + const { colorMode } = useColorMode(); // 'light' or 'dark' + + const isNumeric = (columnType: string) => { + return columnType.startsWith('UInt') || columnType.startsWith('Int') || columnType.startsWith('Float') || columnType.startsWith('Decimal'); + } + + const isHyperlink = (value: string | null) => { + return value && value.startsWith('http'); + } + + const extreme = useMemo(() => { + if (!response) return {}; + let res: Record = {}; + for (let i = 0; i < response.meta.length; i++) { + const columnType = response.meta[i].type; + const columnName = response.meta[i].name; + if (isNumeric(columnType)) { + const values = response.data.map(item => item[columnName]); + const max = Math.max(...values); + const min = Math.min(...values); + res[columnName] = { max, min }; + } + } + return res; + }, [response]); + + + const cellValue = useCallback( + (rowIndex: number, columnIndex: number): string | null => { + const columnName = response?.meta?.[columnIndex]?.name + const cellData = columnName + ? response?.data?.[rowIndex - 1]?.[columnName] + : '' + if (cellData === null || cellData === undefined) { + return null + } + + return typeof cellData === 'object' + ? JSON.stringify(cellData) + : cellData.toString() + }, + [response?.data, response?.meta] + ) + + const onCellClick = (e: any) => { + e.stopPropagation() + if (e.detail > 1) { + const cell = (e.target as HTMLElement).closest( + '[data-grid-row][data-grid-column]' + ) + if (cell && cell.dataset.gridRow && cell.dataset.gridColumn) { + const value = cellValue( + Number(cell.dataset.gridRow), + Number(cell.dataset.gridColumn) + ) + isHyperlink(value) && window.open(value || '', '_blank') + // props.handleCellClick(value) + } + } + } + + const Cell: CellProps = ({ + type, + rowIndex, + columnIndex, + isScrolling, + width, + ...props + }) => { + const columnType = response?.meta?.[columnIndex]?.type || '' + const columnName = response?.meta?.[columnIndex]?.name || '' + if (type === 'header-cell') { + return ( + + + + {response?.meta?.at(columnIndex)?.name} + + + + {response?.meta?.at(columnIndex)?.name} -{' '} + {response?.meta?.at(columnIndex)?.type} + + + ) + } + + const textAlign = columnType && isNumeric(columnType) ? "right" : "left"; + const value = cellValue(rowIndex, columnIndex); + + if (isNumeric(columnType) && response && response.data.length > 1) { + const ratio = value ? 100 * Number(value) / Number(extreme[columnName].max) : 100; + const bgColor = + colorMode === 'light' + ? rowIndex === selectedCell.row + ? '#f0f0f0' + : '#ffffff' + : rowIndex === selectedCell.row + ? 'lch(15.8 0 0)' + : '#1f201b' + + const barColor = colorMode === 'light' ? '#d2d2d2' : '#35372f' + + const background = `linear-gradient(to right, ${barColor} 0%, ${barColor} ${ratio}%, ${bgColor} ${ratio}%, ${bgColor} 100%)` + + return ( + {getValueOfCell(cellValue(rowIndex, columnIndex), false, width)} + ) + } else if (isHyperlink(value)) { + return ( + + {getValueOfCell(cellValue(rowIndex, columnIndex), false, width)} + + ) + } else { + return ( + + {getValueOfCell(cellValue(rowIndex, columnIndex), false, width)} + + ) + } + } + + const getMenuOptions = (selection: SelectedRegion, focus: SelectionFocus) => { + return [ + { + label: 'Copy to TSV', + onSelect: () => { + onCopyTSV(selection, focus) + } + } + ] + } + + const onCopyTSV = (selection: SelectedRegion, focus: SelectionFocus) => { + try { + copyGridElements({ + cell: Cell, + selection: selection, + focus: focus, + rowCount: response ? response.rows : 0, + columnCount: response?.meta?.length ?? 0, + outerRef: gridRef, + columnNames: response?.meta ? response.meta : [], + type: 'tsv' + }) + + createToast({ + title: 'Copied TSV successfully', + description: 'Now you can paste the content', + type: 'success' + }) + } catch (e) { + console.error(e) + + createToast({ + title: 'Failed to copy', + description: + 'Encountered an error while copying. Try again after sometime', + type: 'danger' + }) + } + } + + const onGridCopyMarkdown = ( + selection: SelectedRegion, + focus: SelectionFocus + ) => { + try { + copyGridElements({ + cell: Cell, + selection: selection, + focus: focus, + rowCount: response ? response.rows : 0, + columnCount: response?.meta?.length ?? 0, + outerRef: gridRef, + columnNames: response?.meta ? response.meta : [], + type: 'markdown' + }) + + createToast({ + title: 'Copied markdown successfully', + description: 'Now you can paste the content', + type: 'success' + }) + } catch (e) { + console.error(e) + + createToast({ + title: 'Failed to copy', + description: + 'Encountered an error while copying. Try again after sometime', + type: 'danger' + }) + } + } + + if (props.queryRunning) { + return ( +
+ +
+ ) + } + + let results = null + if (props.view == DefaultView.Chart && props.chart) { + results = + } else if (props.view == DefaultView.Table) { + results = response && response.rows > 0 && ( + <> + {}} + onCopy={onGridCopyMarkdown} + getMenuOptions={getMenuOptions} + className='scrollbar scrollbar-thin scrollbar-thumb-neutral-700 scrollbar-track-neutral-725'> + + ) + } + const height_table = props.chart + ? 300 + : Math.min(Math.ceil(((response?.rows || 0) + 1) * 33), 300) + + return ( + <> + {error ? ( +
+ +

+ An error occurred while processing your request. Please check the + browser console for more details. +

+
+ ) : ( +
+ {results} +
+ )} + + ) +} + +export default CodeResults diff --git a/src/components/CodeViewer/charts/bar/index.tsx b/src/components/CodeViewer/charts/bar/index.tsx new file mode 100644 index 00000000000..0bb0a6c0c0b --- /dev/null +++ b/src/components/CodeViewer/charts/bar/index.tsx @@ -0,0 +1,271 @@ +'use client' +import ReactECharts, { EChartsOption } from 'echarts-for-react' +import type { XAXisOption, YAXisOption } from 'echarts/types/dist/shared' +import isEqual from 'lodash/isEqual' +import { useEffect, useMemo, useState } from 'react' +import { ChartConfig, Column } from '../../types' +import { nonNullType, roundToDynamicPrecision } from '../../utils' + +const MAX_SERIES = 9 + +function getSupportedColumns(columns: Column[]): { + xaxis: string[] + yaxis: string[] + series: string[] +} { + return { + xaxis: columns + .filter( + (col) => + nonNullType(col.type).startsWith('Date') || + nonNullType(col.type).includes('String') || + nonNullType(col.type).startsWith('UInt') || + nonNullType(col.type).startsWith('Int') || + nonNullType(col.type).startsWith("Enum") || + nonNullType(col.type).startsWith("LowCardinality") + ) + .map((col) => col.name), + yaxis: columns + .filter( + (col) => + nonNullType(col.type).startsWith('UInt') || + nonNullType(col.type).startsWith('Int') || + nonNullType(col.type).startsWith('Decimal') || + nonNullType(col.type).startsWith('Float') + ) + .map((col) => col.name), + series: columns + .filter((col) => + nonNullType(col.type).includes("String") || + nonNullType(col.type).startsWith("Enum") || + nonNullType(col.type).startsWith("LowCardinality") + ) + .map((col) => col.name) + } +} + +export default function Bar(props: { + data: Record[] + config: ChartConfig + horizontal?: boolean + columns: Column[] +}) { + const columns = getSupportedColumns(props.columns) + const xaxis = + props.config.xaxis && columns.xaxis.includes(props.config.xaxis) + ? props.config.xaxis + : undefined + const yaxis = + props.config.yaxis && columns.yaxis.includes(props.config.yaxis) + ? props.config.yaxis + : undefined + const series_col = + props.config.series && columns.series.includes(props.config.series) + ? props.config.series + : undefined + + // Declare hooks at the top level unconditionally + const [windowWidth, setWindowWidth] = useState(window.innerWidth) + + useEffect(() => { + const handleResize = () => setWindowWidth(window.innerWidth) + window.addEventListener('resize', handleResize) + return () => window.removeEventListener('resize', handleResize) + }, []) + + const xAxis = useMemo(() => { + if (xaxis === undefined || yaxis === undefined) { + return [] + } + return Array.from(new Set(props.data.map((p) => p[xaxis!]))) + }, [props.data, props.config]) + + const values = useMemo(() => { + if (xaxis === undefined || yaxis === undefined) { + return {} + } + let series_count = 0 + const initialValues = props.data.reduce( + (accumulator, val) => { + const seriesName = + series_col && series_col in val ? val[series_col] : 'all' + if (!(seriesName in accumulator)) { + if (series_count < MAX_SERIES) { + accumulator[seriesName] = { + name: seriesName, + data: new Array(xAxis.length).fill(0) + } + } else { + accumulator['_other_'] = { + name: '_other_', + data: new Array(xAxis.length).fill(0) + } + } + series_count++ + } + return accumulator + }, + {} as Record + ) + + props.data.forEach((p) => { + const seriesName = series_col && series_col in p ? p[series_col] : 'all' + if (seriesName in initialValues) { + initialValues[seriesName].data[xAxis.indexOf(p[xaxis!])] = p[yaxis!] + } else { + initialValues['_other_'].data[xAxis.indexOf(p[xaxis!])] = p[yaxis!] + } + }) + + return initialValues + }, [props.data, props.config, xAxis]) + + const colors = useMemo( + () => [ + '#faff69', + '#FC74FF', + '#66ff73', + '#6df8e1', + '#33e4ff', + '#6d9bf3', + '#cc66ff', + '#fb63d6', + '#fdcf33', + '#fd9050', + '#fd7575', + '#b3b6bd' + ], + [] + ) + + const series:any = useMemo(() => { + const mappedColors: { [key: string]: string } = {} + return Object.values(values).map((series, i) => { + let color = colors[i % colors.length] + if (series.name in mappedColors) { + color = mappedColors[series.name] + } else { + mappedColors[series.name] = color + } + return props.config.stack + ? { + type: 'bar', + name: series.name, + data: series.data, + color: color, + stack: 'series' + } + : { + type: 'bar', + name: series.name, + data: series.data, + color: color + } + }) + }, [values, props.config.stack, colors]) + + if (xaxis === undefined || yaxis === undefined) { + return <> + } + const categoryAxis = { + show: true, + type: 'category', + data: xAxis, + axisLabel: { + hideOverlap: true + } + } + + const numberAxis = { + splitLine: { + show: true, + lineStyle: { + color: '#808691', + opacity: 0.3 + } + }, + axisLabel: { + hideOverlap: true + } + } + + const longestLabelLength = props.horizontal + ? Math.max(...xAxis.map((label) => String(label).length), 0) + : 0 + const leftPadding = props.horizontal + ? Math.max(48, longestLabelLength * 7) // Estimate width based on character count + : 24 + + const bottomPadding = windowWidth >= 1536 && series.length > 1 ? '48px' : '12px' + const options: EChartsOption = { + title: { + text: props.config.title, + textStyle: { + width: '100%', + fontSize: 16, + color: '#808691', + fontWeight: 'normal' + }, + left: 'center' + }, + animation: false, + grid: { + left: '8px', + right: '8px', + bottom: bottomPadding, + top: '24px', + containLabel: true + }, + tooltip: { + trigger: 'item', + textStyle: { + color: '#FAFF69', + fontWeight: 'bold', + fontSize: 16, + lineHeight: 24 + }, + backgroundColor: '#181818', + borderWidth: 0, + valueFormatter: (value: any) => + roundToDynamicPrecision(value as number).toString() + }, + xAxis: props.horizontal + ? (numberAxis as XAXisOption) + : (categoryAxis as XAXisOption), + legend: + windowWidth >= 1536 && series_col + ? { + bottom: '0%', + right: '0px', + orient: 'horizontal', + textStyle: { + color: '#FFFFFFF', + fontSize: 14 + }, + icon: 'circle', + borderRadius: 5, + borderWidth: 1, + borderColor: '#626262', + padding: 10 + } + : undefined, + yAxis: props.horizontal + ? (categoryAxis as YAXisOption) + : (numberAxis as YAXisOption), + series: series + } + + return ( +
+ { + return !isEqual(prevProps.option, currentProps.option) + }} + /> +
+ ) +} diff --git a/src/components/CodeViewer/charts/candlestick/index.tsx b/src/components/CodeViewer/charts/candlestick/index.tsx new file mode 100644 index 00000000000..b64cf4ed632 --- /dev/null +++ b/src/components/CodeViewer/charts/candlestick/index.tsx @@ -0,0 +1,231 @@ +'use client' +import EChartsReact from 'echarts-for-react' +import isEqual from 'lodash/isEqual' +import { useMemo, useRef } from 'react' +import { Column, XAxisConfig, YAxisConfig } from '../../types' +import { nonNullType, roundByScale, roundToDynamicPrecision } from '../../utils' +/* + +example query to generate data: + +SELECT + toDateTime('2024-01-01 00:00:00') + INTERVAL number WEEK AS timestamp, any(close) OVER (ORDER BY timestamp ASC ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS open, + round(randNormal(1000, 100), 2) AS close, + min2 (open, close) - round(randNormal(0, 10), 2) AS low, + max2 (open, close) + round(randNormal(0, 10), 2) AS high +FROM + numbers(0, 52) +ORDER BY + timestamp; +*/ + +function getSupportedColumns(columns: Column[]): { + xaxis: string[] + open: string[] + close: string[] + high: string[] + low: string[] +} { + return { + xaxis: columns + .filter( + (col) => + nonNullType(col.type).startsWith('Date') || + nonNullType(col.type).startsWith('UInt') || + nonNullType(col.type).startsWith('Int') + ) + .map((col) => col.name), + open: columns + .filter( + (col) => + nonNullType(col.type).startsWith('UInt') || + nonNullType(col.type).startsWith('Int') || + nonNullType(col.type).startsWith('Decimal') || + nonNullType(col.type).startsWith('Float') + ) + .map((col) => col.name), + close: columns + .filter( + (col) => + nonNullType(col.type).startsWith('UInt') || + nonNullType(col.type).startsWith('Int') || + nonNullType(col.type).startsWith('Decimal') || + nonNullType(col.type).startsWith('Float') + ) + .map((col) => col.name), + low: columns + .filter( + (col) => + nonNullType(col.type).startsWith('UInt') || + nonNullType(col.type).startsWith('Int') || + nonNullType(col.type).startsWith('Decimal') || + nonNullType(col.type).startsWith('Float') + ) + .map((col) => col.name), + high: columns + .filter( + (col) => + nonNullType(col.type).startsWith('UInt') || + nonNullType(col.type).startsWith('Int') || + nonNullType(col.type).startsWith('Decimal') || + nonNullType(col.type).startsWith('Float') + ) + .map((col) => col.name) + } +} + +export type CandleStickChartConfig = XAxisConfig & + YAxisConfig & { + title?: string + high?: string + low?: string + open?: string + close?: string + } + +const CandleStickChart = (props: { + data: Record[] + config: CandleStickChartConfig + columns: Column[] +}) => { + const columns = getSupportedColumns(props.columns) + const xaxis = + props.config.xaxis && columns.xaxis.includes(props.config.xaxis) + ? props.config.xaxis + : undefined + const high = + props.config.high && columns.high.includes(props.config.high) + ? props.config.high + : undefined + const low = + props.config.low && columns.low.includes(props.config.low) + ? props.config.low + : undefined + const open = + props.config.open && columns.high.includes(props.config.open) + ? props.config.open + : undefined + const close = + props.config.close && columns.close.includes(props.config.close) + ? props.config.close + : undefined + + const chartRef = useRef(null) + const xAxis = useMemo(() => { + if ( + xaxis === undefined || + high === undefined || + low === undefined || + open === undefined || + close === undefined + ) { + return [] + } + return Array.from(new Set(props.data.map((p) => p[xaxis!]))) + }, [props.data, xaxis, high, low, open, close]) + + const values = useMemo(() => { + if ( + xaxis === undefined || + high === undefined || + low === undefined || + open === undefined || + close === undefined + ) { + return [] + } + const initialValues = props.data.map((p) => [ + p[open], + p[close], + p[low], + p[high] + ]) + return initialValues + }, [props.data, props.config]) + + const options:any = { + title: { + text: props.config.title, + textStyle: { + width: '100%', + fontSize: 16, + color: '#808691', + fontWeight: 'normal' + }, + left: 'center' + }, + animation: false, + grid: { + left: '36px', + right: '16x', + bottom: '24px', + top: '24px' + }, + xAxis: { + show: true, + type: 'category', + data: xAxis, + nameLocation: 'middle', + min: 0, + max: xAxis.length - 1 + }, + yAxis: { + type: 'value', + splitLine: { + show: true, + lineStyle: { + color: '#808691', + opacity: 0.3 + } + }, + min: (value: { min: number; max: number }) => { + return props.config.yaxis_min !== undefined + ? props.config.yaxis_min + : roundByScale(value.min * 0.9) + }, + max: props.config.yaxis_max ? props.config.yaxis_max : undefined + }, + series: { + type: 'candlestick', + data: values, + itemStyle: { + color: '#99FFA1', + borderColor: '#99FFA1', + color0: '#FF7575', + borderColor0: '#FF7575' + } + }, + tooltip: { + backgroundColor: '#302e32', + borderColor: '#302e32 transparent transparent transparent', + textStyle: { + color: '#FAFF69', + fontWeight: 'bold', + fontSize: 16, + lineHeight: 24 + }, + trigger: 'axis', + axisPointer: { + axis: 'x' + }, + valueFormatter: (value: number) => roundToDynamicPrecision(value) + } + } + + return ( +
+ { + return !isEqual(prevProps, currentProps) + }} + /> +
+ ) +} + +export default CandleStickChart diff --git a/src/components/CodeViewer/charts/heatmap/index.tsx b/src/components/CodeViewer/charts/heatmap/index.tsx new file mode 100644 index 00000000000..48654671382 --- /dev/null +++ b/src/components/CodeViewer/charts/heatmap/index.tsx @@ -0,0 +1,260 @@ +'use client' +import { + default as EChartsReact, + default as ReactECharts +} from 'echarts-for-react' +import isEqual from 'lodash/isEqual' +import { useMemo, useRef } from 'react' +import { ChartConfig, Column } from '../../types' +import { nonNullType } from '../../utils' +import styles from './styles.module.css' + +const MAX_SERIES = 9 + +export type HeatmapChartConfig = ChartConfig & { + zaxis?: string +} + +function getSupportedColumns(columns: Column[]): { + xaxis: string[] + yaxis: string[] + zaxis: string[] +} { + return { + xaxis: columns + .filter( + (col) => + nonNullType(col.type).includes('String') || + nonNullType(col.type).startsWith("Enum") || + nonNullType(col.type).startsWith("LowCardinality") || + nonNullType(col.type).includes('Date') + ) + .map((col) => col.name), + yaxis: columns + .filter( + (col) => + nonNullType(col.type).includes('String') || + nonNullType(col.type).startsWith("Enum") || + nonNullType(col.type).startsWith("LowCardinality") || + nonNullType(col.type).includes('Date') + ) + .map((col) => col.name), + zaxis: columns + .filter( + (col) => + nonNullType(col.type).startsWith('UInt') || + nonNullType(col.type).startsWith('Int') || + nonNullType(col.type).startsWith('Decimal') || + nonNullType(col.type).startsWith('Float') + ) + .map((col) => col.name) + } +} + +export default function HeatMap(props: { + data: Record[] + config: HeatmapChartConfig + columns: Column[] +}) { + const columns = getSupportedColumns(props.columns) + const xaxis = + props.config.xaxis && columns.xaxis.includes(props.config.xaxis) + ? props.config.xaxis + : undefined + const yaxis = + props.config.yaxis && columns.yaxis.includes(props.config.yaxis) + ? props.config.yaxis + : undefined + const zaxis = + props.config.zaxis && columns.zaxis.includes(props.config.zaxis) + ? props.config.zaxis + : undefined + + const chartRef = useRef(null) + + const xValues = useMemo(() => { + if (xaxis === undefined || yaxis === undefined || zaxis === undefined) { + return [] + } + return props.data + .map((p) => p[xaxis!]) + .filter((item, pos, ary) => { + return !pos || item != ary[pos - 1] + }) + }, [props.data, props.config]) + + const yValues = useMemo(() => { + if (xaxis === undefined || yaxis === undefined || zaxis === undefined) { + return [] + } + return props.data + .map((p) => p[yaxis!]) + .sort() + .filter((item, pos, ary) => { + return !pos || item != ary[pos - 1] + }) + }, [props.data, props.config]) + + const values = useMemo(() => { + if (xaxis === undefined || yaxis === undefined || zaxis === undefined) { + return {} + } + return props.data.reduce( + (vals, val) => { + val[xaxis!] in vals + ? (vals[val[xaxis!]][val[yaxis!]] = Number(val[zaxis!])) + : (vals[val[xaxis!]] = { [val[yaxis!]]: Number(val[zaxis!]) }) + return vals + }, + {} as Record + ) + }, [props.data, props.config]) + + const longestYLabelLength = Math.max( + ...yValues.map((label) => String(label).length), + 0 + ) + const leftPadding = longestYLabelLength * 8 + + const options:any = { + title: { + text: props.config.title, + textStyle: { + width: '100%', + fontSize: 16, + color: '#808691', + fontWeight: 'normal' + }, + left: 'center' + }, + grid: { + left: `${leftPadding}px`, + right: '16px', + bottom: '36px', + top: '32px' + }, + xAxis: { + type: 'category', + offset: 15, + data: xValues, + splitArea: { + show: true + }, + axisLine: { + onZero: false + } + }, + toolBox: { + show: false + }, + yAxis: { + type: 'category', + offset: 15, + splitArea: { + show: true + }, + data: yValues, + axisLine: { + onZero: false + } + }, + tooltip: { + trigger: 'item', + textStyle: { + color: '#FAFF69', + fontWeight: 'bold', + fontSize: 16, + lineHeight: 24 + }, + backgroundColor: '#181818', + borderWidth: 0, + formatter: (params: any) => { + return `
${ + yValues[params.value[1]] + } - ${xValues[params.value[0]]} - ${Number( + params.value[2] + ).toLocaleString('en-US')} +
` + }, + position: (point: any, params: any, dom: any, rect: any, size: any) => { + const echartsInstance = chartRef.current?.getEchartsInstance() + if (echartsInstance) { + const pos = echartsInstance.convertToPixel({ seriesIndex: 0 }, [ + params.value[0], + params.value[1] + ]) + const xOffset = pos[0] - size.contentSize[1] * 2 + const yOffset = pos[1] - size.contentSize[1] - 10 + return [xOffset, yOffset] + } + return [point[0], point[1]] + }, + extraCssText: 'visibility: hidden;padding:0px;' + }, + visualMap: { + min: Math.min( + ...props.data.map((p) => + props.config.zaxis ? p[props.config.zaxis] : 0 + ) + ), + max: Math.max( + ...props.data.map((p) => + props.config.zaxis ? p[props.config.zaxis] : 0 + ) + ), + calculable: true, + orient: 'horizontal', + color: [ + 'rgba(252, 255, 116, 1)', + 'rgba(252, 255, 116, 0.9)', + 'rgba(252, 255, 116, 0.8)', + 'rgba(252, 255, 116, 0.7)', + 'rgba(252, 255, 116, 0.6)', + 'rgba(252, 255, 116, 0.5)', + 'rgba(252, 255, 116, 0.4)', + 'rgba(252, 255, 116, 0.3)', + 'rgba(252, 255, 116, 0.2)', + 'rgba(252, 255, 116, 0.1)', + '#262626' + ], + show: false + }, + series: [ + { + type: 'heatmap', + data: xValues + .map((x, xi) => + yValues.map((y, yi) => [xi, yi, y in values[x] ? values[x][y] : 0]) + ) + .flat(), + label: { + show: false + }, + emphasis: { + itemStyle: { + color: '#FAFF69' // Color on emphasis for better visibility + } + }, + itemStyle: { + borderWidth: 1, + borderColor: '#262626' // Set a distinct border to see each cell + } + } + ], + legend: null + } + + return ( +
+ { + return !isEqual(prevProps, currentProps) + }} + /> +
+ ) +} diff --git a/src/components/CodeViewer/charts/heatmap/styles.module.css b/src/components/CodeViewer/charts/heatmap/styles.module.css new file mode 100644 index 00000000000..82aca2f9557 --- /dev/null +++ b/src/components/CodeViewer/charts/heatmap/styles.module.css @@ -0,0 +1,31 @@ +.tooltip { + position: relative; + visibility: visible; + display: inline-block; +} + +.tooltip .tooltiptext { + visibility: visible; + background-color: #302e32; + text-align: center; + border-radius: 6px; + padding: 5px; + position: absolute; + z-index: 1; + margin-left: -64px; + margin-top: -20px; + +} + +.tooltip .tooltiptext::after { + content: ""; + visibility: visible; + position: absolute; + background-color: transparent; + top: 100%; + left: 50%; + margin-left: -5px; + border-width: 10px; + border-style: solid; + border-color: #302e32 transparent transparent transparent; +} diff --git a/src/components/CodeViewer/charts/index.tsx b/src/components/CodeViewer/charts/index.tsx new file mode 100644 index 00000000000..c211adeddca --- /dev/null +++ b/src/components/CodeViewer/charts/index.tsx @@ -0,0 +1,122 @@ +import { Panel } from '@clickhouse/click-ui/bundled' +import { useEffect, useState } from 'react' +import { ChartConfig, ChartType, QueryResults } from '../types' +import BarChart from './bar' +import CandleStickChart from './candlestick' +import HeatMap from './heatmap' +import LineChart from './line' +import PieChart from './pie' + +const chartTypes = Object.values(ChartType) + +const Chart = (props: { + results?: QueryResults + chart: { type: ChartType; config?: ChartConfig } +}) => { + const [windowWidth, setWindowWidth] = useState(0) + + useEffect(() => { + const handleResize = () => setWindowWidth(window.innerWidth) + handleResize() + window.addEventListener('resize', handleResize) + return () => window.removeEventListener('resize', handleResize) + }, []) + + if (!props.results) { + return
+ } + + let chart = null + + if (props.chart.config) { + switch (props.chart.type) { + case ChartType.Line: + case ChartType.Area: + case ChartType.Scatter: { + chart = ( +
+ +
+ ) + break + } + case ChartType.HorizontalBar: + case ChartType.Bar: { + chart = ( +
+ +
+ ) + break + } + case ChartType.Pie: { + chart = ( +
+ +
+ ) + break + } + case ChartType.HeatMap: { + chart = ( +
+ +
+ ) + break + } + case ChartType.CandleStick: { + chart = ( +
+ +
+ ) + break + } + default: { + chart = null + } + } + } + + return ( +
+ + {chart} + +
+ ) +} + +export default Chart diff --git a/src/components/CodeViewer/charts/line/index.tsx b/src/components/CodeViewer/charts/line/index.tsx new file mode 100644 index 00000000000..75ada112771 --- /dev/null +++ b/src/components/CodeViewer/charts/line/index.tsx @@ -0,0 +1,357 @@ +'use client' +import EChartsReact from 'echarts-for-react' +import isEqual from 'lodash/isEqual' +import { useEffect, useMemo, useRef, useState } from 'react' +import { ChartConfig, Column } from '../../types' +import { nonNullType, roundByScale, roundToDynamicPrecision } from '../../utils' +import styles from './styles.module.css' + +const MAX_SERIES = 9 + +function getSupportedColumns(columns: Column[]): { + xaxis: string[] + yaxis: string[] + series: string[] +} { + return { + xaxis: columns + .filter( + (col) => + nonNullType(col.type).startsWith('Date') || + nonNullType(col.type).startsWith('UInt') || + nonNullType(col.type).startsWith('Int') + ) + .map((col) => col.name), + yaxis: columns + .filter( + (col) => + nonNullType(col.type).startsWith('UInt') || + nonNullType(col.type).startsWith('Int') || + nonNullType(col.type).startsWith('Decimal') || + nonNullType(col.type).startsWith('Float') + ) + .map((col) => col.name), + series: columns + .filter((col) => + nonNullType(col.type).includes("String") || + nonNullType(col.type).startsWith("Enum") || + nonNullType(col.type).startsWith("LowCardinality") + ) + .map((col) => col.name) + } +} + +const LineChart = (props: { + data: Record[] + config: ChartConfig + scatter?: boolean + fill_area?: boolean + columns: Column[] +}) => { + const columns = getSupportedColumns(props.columns) + const xaxis = + props.config.xaxis && columns.xaxis.includes(props.config.xaxis) + ? props.config.xaxis + : undefined + const yaxis = + props.config.yaxis && columns.yaxis.includes(props.config.yaxis) + ? props.config.yaxis + : undefined + const series_col = + props.config.series && columns.series.includes(props.config.series) + ? props.config.series + : undefined + + const chartRef = useRef(null) + const [windowWidth, setWindowWidth] = useState(window.innerWidth) + + useEffect(() => { + const handleResize = () => setWindowWidth(window.innerWidth) + window.addEventListener('resize', handleResize) + return () => window.removeEventListener('resize', handleResize) + }, []) + + const xAxis = useMemo(() => { + if (xaxis === undefined || yaxis === undefined) { + return [] + } + return Array.from(new Set(props.data.map((p) => p[xaxis!]))) + }, [props.data, xaxis, yaxis]) + + const values = useMemo(() => { + if (xaxis === undefined || yaxis === undefined) { + return {} + } + let series_count = 0 + const initialValues = props.data.reduce( + (accumulator, val) => { + const seriesName = + series_col && series_col in val ? val[series_col] : 'all' + if (!(seriesName in accumulator)) { + if (series_count < MAX_SERIES) { + accumulator[seriesName] = { + name: seriesName, + data: new Array(xAxis.length).fill(0) + } + } else { + accumulator['_other_'] = { + name: '_other_', + data: new Array(xAxis.length).fill(0) + } + } + series_count++ + } + return accumulator + }, + {} as Record + ) + + props.data.forEach((p) => { + const seriesName = series_col && series_col in p ? p[series_col] : 'all' + if (seriesName in initialValues) { + initialValues[seriesName].data[xAxis.indexOf(p[xaxis!])] = p[yaxis!] + } else { + initialValues['_other_'].data[xAxis.indexOf(p[xaxis!])] = p[yaxis!] + } + }) + return initialValues + }, [props.data, props.config]) + + const colors = useMemo( + () => [ + '#faff69', + '#FC74FF', + '#66ff73', + '#6df8e1', + '#33e4ff', + '#6d9bf3', + '#cc66ff', + '#fb63d6', + '#fdcf33', + '#fd9050', + '#fd7575', + '#b3b6bd' + ], + [] + ) + + const series = useMemo(() => { + const mappedColors: { [key: string]: string } = {} + + return Object.values(values).map((series, i) => { + let color = colors[i % colors.length] + if (series.name in mappedColors) { + color = mappedColors[series.name] + } else { + mappedColors[series.name] = color + } + return { + name: series.name, + data: series.data, + type: props.scatter ? 'scatter' : 'line', + smooth: true, + showSymbol: props.scatter ? true : false, + symbolSize: 6, + areaStyle: props.fill_area ? {} : null, + lineStyle: { + color: color, + width: 1.5 + }, + itemStyle: { + color: color + } + } + }) + }, [values, props.config, colors, props.fill_area, props.scatter]) + + if (xaxis === undefined || yaxis === undefined) { + return <> + } + + const onMouseOver = () => { + const echartsInstance = chartRef.current?.getEchartsInstance() + if ( + echartsInstance && + !props.fill_area && + !props.scatter && + series.length === 1 + ) { + const newOptions = { + series: [ + { + lineStyle: { + opacity: 1, + shadowColor: '#FAFF69', + shadowOffsetX: 0, + shadowOffsetY: 0, + shadowBlur: 0 + }, + areaStyle: { + color: { + type: 'linear', + x: 0, + y: 0, + x2: 0, + y2: 0.65, + colorStops: [ + { + offset: 0, + color: '#FAFF69' + }, + { + offset: 1, + color: '#343431' + } + ] + }, + opacity: 0.1 + } + } + ] + } + echartsInstance.setOption(newOptions) + echartsInstance.dispatchAction({ + type: 'takeGlobalCursor', + key: 'brush', + brushOption: { + brushType: 'lineX' + } + }) + } + } + + const bottomPadding = windowWidth >= 1536 && series.length > 1 ? '48px' : '12px' + + const options: any = { + animation: false, + title: { + text: props.config.title, + textStyle: { + width: '100%', + fontSize: 16, + color: '#808691', + fontWeight: 'normal' + }, + left: 'center' + }, + grid: { + left: '8px', + right: '8px', + bottom: bottomPadding, + top: '36px', + containLabel: true + }, + xAxis: { + show: true, + type: 'category', + boundaryGap: false, + data: xAxis, + nameLocation: 'middle', + min: 0, + max: xAxis.length - 1, + axisLabel: { + hideOverlap: true + } + }, + yAxis: { + type: 'value', + splitLine: { + show: true, + lineStyle: { + color: '#808691', + opacity: 0.3 + } + }, + min: (value: { min: number; max: number }) => { + return props.config.yaxis_min !== undefined + ? props.config.yaxis_min + : roundByScale(value.min * 0.9) + }, + max: props.config.yaxis_max ? props.config.yaxis_max : undefined + }, + series: series, + legend: + windowWidth >= 1536 && series_col + ? { + bottom: '0%', + right: '0px', + orient: 'horizontal', + textStyle: { + color: '#FFFFFFF', + fontSize: 14 + }, + icon: 'circle', + borderRadius: 5, + borderWidth: 1, + borderColor: '#626262', + padding: 10 + } + : null, + tooltip: { + trigger: 'axis', + textStyle: { + color: '#FAFF69', + fontWeight: 'bold', + fontSize: 16, + lineHeight: 16 + }, + backgroundColor: '#302e32', + borderWidth: 0, + valueFormatter: (value: number) => roundToDynamicPrecision(value), + ...(series.length === 1 && { + // Only include these properties if series length is equal to 1 + formatter: (params: any) => { + return `
+ ${params[0].axisValue}: ${roundToDynamicPrecision( + params[0].value + ).toLocaleString('en-US')} +
` + }, + extraCssText: 'visibility: hidden;padding:0px;', + position: (point: any, params: any, dom: any, rect: any, size: any) => { + const echartsInstance = chartRef.current?.getEchartsInstance() + if (echartsInstance) { + const pos = echartsInstance.convertToPixel({ seriesIndex: 0 }, [ + params[0].axisValue, + params[0].value + ]) + return [pos[0], pos[1] - size.contentSize[1] * 2] + } + } + }) + } + // brush: { + // toolbox: ["lineX", "clear"], + // brushType: "lineX", + // brushMode: "single", + // transformable: false + // } + } + + const onMouseOut = () => { + const echartsInstance = chartRef.current?.getEchartsInstance() + if (echartsInstance) { + echartsInstance.setOption(options) + } + } + + return ( +
+ { + return !isEqual(prevProps, currentProps) + }} + /> +
+ ) +} + +export default LineChart diff --git a/src/components/CodeViewer/charts/line/styles.module.css b/src/components/CodeViewer/charts/line/styles.module.css new file mode 100644 index 00000000000..5fdc3f546fa --- /dev/null +++ b/src/components/CodeViewer/charts/line/styles.module.css @@ -0,0 +1,37 @@ +.tooltip { + position: relative; + visibility: visible; + display: inline-block; +} + +.tooltip .tooltiptext { + visibility: visible; + background-color: #302e32; + text-align: center; + border-radius: 6px; + padding: 5px; + position: absolute; + z-index: 1; + margin-left: -64px; + margin-top: -20px; +} + +.tooltip .tooltiptext::after { + content: ''; + visibility: visible; + position: absolute; + background-color: transparent; + top: 100%; + left: 50%; + margin-left: -5px; + border-width: 10px; + border-style: solid; + border-color: #302e32 transparent transparent transparent; +} + +.line { + height: 100%; + display: flex; + flex-direction: column; + justify-content: space-between; +} \ No newline at end of file diff --git a/src/components/CodeViewer/charts/pie/index.tsx b/src/components/CodeViewer/charts/pie/index.tsx new file mode 100644 index 00000000000..b76440d9958 --- /dev/null +++ b/src/components/CodeViewer/charts/pie/index.tsx @@ -0,0 +1,183 @@ +'use client' +import ReactECharts from 'echarts-for-react' +import { useEffect, useMemo, useState } from 'react' +import { ChartConfig, Column } from '../../types' +import { nonNullType, roundToDynamicPrecision } from '../../utils' + +const MAX_SERIES = 9 + +function getSupportedColumns(columns: Column[]): { + xaxis: string[] + yaxis: string[] +} { + return { + xaxis: columns + .filter((col) => nonNullType(col.type).includes("String") || + nonNullType(col.type).startsWith("Enum") || + nonNullType(col.type).startsWith("LowCardinality")) + .map((col) => col.name), + yaxis: columns + .filter( + (col) => + nonNullType(col.type).startsWith('UInt') || + nonNullType(col.type).startsWith('Int') || + nonNullType(col.type).startsWith('Decimal') || + nonNullType(col.type).startsWith('Float') + ) + .map((col) => col.name) + } +} + +export default function Pie(props: { + data: Record[] + config: ChartConfig + columns: Column[] +}) { + const columns = getSupportedColumns(props.columns) + // unset columns which aren't valid + const xaxis = + props.config.xaxis && columns.xaxis.includes(props.config.xaxis) + ? props.config.xaxis + : undefined + const yaxis = + props.config.yaxis && columns.yaxis.includes(props.config.yaxis) + ? props.config.yaxis + : undefined + // Declare hooks at the top level unconditionally + const [windowWidth, setWindowWidth] = useState(window.innerWidth) + useEffect(() => { + const handleResize = () => setWindowWidth(window.innerWidth) + window.addEventListener('resize', handleResize) + return () => window.removeEventListener('resize', handleResize) + }, []) + + const values = useMemo(() => { + if (xaxis === undefined || yaxis === undefined) { + return {} + } + let series_count = 0 + return props.data.reduce((accumulator, val) => { + const seriesName = xaxis! in val ? val[xaxis!] : 'all' + if (!(seriesName in accumulator)) { + if (series_count < MAX_SERIES) { + accumulator[seriesName] = Number(val[yaxis!]) + } else if ('_other_' in accumulator) { + accumulator['_other_'] = accumulator['_other_'] + Number(val[yaxis!]) + } else { + accumulator['_other_'] = Number(val[yaxis!]) + } + series_count++ + } else { + accumulator[seriesName] = accumulator[seriesName] + Number(val[yaxis!]) + } + return accumulator + }, {}) + }, [props.data, xaxis, yaxis]) + + const data = useMemo(() => { + return Object.entries(values).map(([seriesName, value]) => { + return { value: value, name: seriesName } + }) + }, [values]) + + if (xaxis === undefined || yaxis === undefined) { + return <> + } + + const options:any = { + title: { + text: props.config.title, + textStyle: { + width: '100%', + fontSize: 16, + color: '#808691', + fontWeight: 'normal' + }, + left: 'center' + }, + animation: false, + grid: { + left: '80px', + right: '24px', + bottom: '24px', + top: '24px' + }, + tooltip: { + trigger: 'item', + textStyle: { + color: '#FAFF69', + fontWeight: 'bold', + fontSize: 16, + lineHeight: 24 + }, + backgroundColor: '#181818', + borderWidth: 0, + valueFormatter: (value: number) => roundToDynamicPrecision(value) + }, + legend: + windowWidth >= 1536 + ? { + top: '0%', + right: '0px', + orient: 'vertical', + textStyle: { + color: '#FFFFFFF', + fontSize: 14 + }, + icon: 'circle', + borderRadius: 5, + borderWidth: 1, + borderColor: '#626262', + padding: 10 + } + : null, + series: [ + { + name: xaxis, + type: 'pie', + radius: ['50%', '90%'], + avoidLabelOverlap: true, + center: ['50%', '55%'], + label: { + show: false + }, + emphasis: { + label: { + show: false + } + }, + labelLine: { + show: true + }, + z: 2, + itemStyle: {}, + color: [ + '#faff69', + '#FC74FF', + '#66ff73', + '#6df8e1', + '#33e4ff', + '#6d9bf3', + '#cc66ff', + '#fb63d6', + '#fdcf33', + '#fd9050', + '#fd7575', + '#b3b6bd' + ], + data: data + } + ] + } + + return ( +
+ +
+ ) +} diff --git a/src/components/CodeViewer/copy/copyGridElements.tsx b/src/components/CodeViewer/copy/copyGridElements.tsx new file mode 100644 index 00000000000..1c366f27df0 --- /dev/null +++ b/src/components/CodeViewer/copy/copyGridElements.tsx @@ -0,0 +1,179 @@ +import { CellProps, SelectedRegion, SelectionFocus } from "@clickhouse/click-ui/bundled"; +import { RefObject, createElement } from "react"; +import { renderToStaticMarkup } from "react-dom/server"; +import { Column } from "../types"; + +interface CopyGridElementsProps { + cell: CellProps; + selection: SelectedRegion; + rowCount: number; + columnCount: number; + pageStart?: number; + focus: SelectionFocus; + outerRef: RefObject; + columnNames: Column[]; + type: String; +} + +function copyTableAsMarkdown(table: HTMLTableElement) { + // Extract header cells from the section if present + const headerCells = table.querySelector("thead") + ? Array.from(table.querySelectorAll("thead th")).map(cell => cell.textContent?.trim() ?? "") + : []; + + let markdownRows: string[] = []; + if (headerCells.length > 0) { + // Create the Markdown header row and separator + const headerRow = `| ${headerCells.join(" | ")} |`; + const separatorRow = `| ${headerCells.map(() => "---").join(" | ")} |`; + markdownRows.push(headerRow) + markdownRows.push(separatorRow) + } + + // Extract body rows, excluding the header row if no is used + const bodyRows = Array.from(table.querySelectorAll("tbody tr")).length + ? Array.from(table.querySelectorAll("tbody tr")) + : [] + + // Map each row to a Markdown formatted row + bodyRows.forEach(row => { + const rowCells: string[] = Array.from(row.querySelectorAll("td, th")).map(cell => cell.textContent?.trim() ?? ""); + markdownRows.push(`| ${rowCells.join(" | ")} |`); + }); + + // Combine the header, separator, and body rows into the final Markdown table + const markdownTable = [...markdownRows].join("\n"); + + // Copy to clipboard + navigator.clipboard.writeText(markdownTable); + +} + +const addCellToRow = ( + row: HTMLTableRowElement, + cell: CellProps, + rowIndex: number, + columnIndex: number +) => { + const td = document.createElement("td"); + // const root = createRoot(td); + const html = renderToStaticMarkup( + createElement(cell, { rowIndex, columnIndex, width: 1000, type: "row-cell" }) + ); + td.innerHTML = html; + + row.appendChild(td); + // root.unmount(); +}; + +const headerListLoop = ( + thead: HTMLTableSectionElement, + columnList: Array, + columnNames: Column[] +) => { + const row = document.createElement("tr"); + columnList.forEach(columnIndex => { + const th = document.createElement("th"); + th.innerHTML = columnNames?.[columnIndex].name; + row.appendChild(th); + }); + thead.appendChild(row); +}; + +const columnListLoop = ( + tbody: HTMLTableSectionElement, + columnList: Array, + cell: CellProps, + rowIndex: number +) => { + const row = document.createElement("tr"); + columnList.forEach(columnIndex => { + addCellToRow(row, cell, rowIndex, columnIndex); + }); + tbody.appendChild(row); +}; + +const copyGridElements = async ({ + cell, + selection, + rowCount, + columnCount, + pageStart = 0, + focus, + outerRef, + columnNames, + type +}: CopyGridElementsProps): Promise => { + if (!outerRef.current) { + throw "Could not fetch selection"; + } + + const table = document.createElement("table"); + table.style.position = "absolute"; + table.style.top = "-200px"; + table.style.left = "-200px"; + table.style.width = "0px"; + table.style.height = "0px"; + table.style.overflow = "hidden"; + const thead = document.createElement("thead"); + const tbody = document.createElement("tbody"); + + switch (selection.type) { + case "rectangle": + const columnListRectangle = Array.from( + { length: selection.bounds.right + 1 - selection.bounds.left }, + (_, index) => selection.bounds.left + index + ); + headerListLoop(thead, columnListRectangle, columnNames); + Array.from( + { length: selection.bounds.bottom + 1 - selection.bounds.top }, + (_, index) => selection.bounds.top + index + ).forEach(rowIndex => { + + columnListLoop(tbody, columnListRectangle, cell, rowIndex); + }); + break; + case "columns": + const columnList = Array.from(selection.columns).sort(); + headerListLoop(thead, columnList, columnNames); + Array.from({ length: rowCount }, (_, index) => pageStart + 1 + index).forEach( + rowIndex => { + columnListLoop(tbody, columnList, cell, rowIndex); + } + ); + break; + case "rows": + const columnListRows = Array.from( + { length: columnCount }, + (_, index) => pageStart + index + ); + headerListLoop(thead, columnListRows, columnNames); + Array.from(selection.rows).sort().forEach(rowIndex => { + columnListLoop(tbody, columnListRows, cell, rowIndex); + }); + break; + case "empty": + columnListLoop(tbody, [focus.column], cell, focus.row); + break; + default: + throw new Error("incorrect selection provided"); + } + + table.appendChild(thead); + table.appendChild(tbody); + + outerRef.current.appendChild(table); + + const windowSelection = window.getSelection(); + if (windowSelection) { + if (type === 'tsv') + await navigator.clipboard.writeText(table.innerText); + else + await copyTableAsMarkdown(table); + outerRef.current.removeChild(table); + } else { + throw "Could not fetch selection"; + } +}; + +export default copyGridElements; diff --git a/src/components/CodeViewer/index.tsx b/src/components/CodeViewer/index.tsx new file mode 100644 index 00000000000..3058a6074d0 --- /dev/null +++ b/src/components/CodeViewer/index.tsx @@ -0,0 +1,128 @@ +import { CodeBlock, ClickUIProvider, Text, Separator } from '@clickhouse/click-ui/bundled' +import CodeInterpreter from './CodeInterpreter' +import { DefaultView } from './CodeResults' +import { ChartConfig, ChartType } from './types' +import { base64Decode } from './utils' +import { useColorMode } from '@docusaurus/theme-common' +import { isValidElement } from 'react' +import DocusaurusCodeBlock from '@theme-original/CodeBlock'; + +function getCodeContent(children: any): string { + if (typeof children === 'string') return children + + if (isValidElement(children) && children.props?.children) { + return getCodeContent(children.props.children) + } + + if (Array.isArray(children)) { + return children.map(getCodeContent).join('') + } + + return '' +} + +function parseInlineStyle(styleString: string): React.CSSProperties { + if (styleString) { + return styleString.split(';').reduce((acc, item) => { + const [key, value] = item.split(':').map(str => str.trim()) + if (key && value) { + const camelKey = key.replace(/-([a-z])/g, (_, char) => char.toUpperCase()) + acc[camelKey] = value + } + return acc + }, {} as React.CSSProperties) + } + return {} +} + +function CodeViewer({ + node, + inline, + className, + language = 'sql', + show_line_numbers = false, + runnable = 'false', + run = 'false', + link, + view = 'table', + chart_config = '', + clickhouse_settings = '{}', + show_statistics = 'true', + style = '', + title='', + click_ui = 'false', + children, + ...props +}: any) { + const showLineNumbers = show_line_numbers === 'true' + const runBoolean = run === 'true' + const runnableBoolean = runnable === 'true' + const showStatistics = show_statistics === 'true' + + let chart: { type: ChartType; config?: ChartConfig } | undefined + try { + const parsedChart = JSON.parse(chart_config !== '' ? base64Decode(chart_config) : '{}') + if (parsedChart && parsedChart.type && parsedChart.config) { + chart = { + type: parsedChart.type as ChartType, + config: parsedChart.config + } + } + } catch { + console.log('chart config is not valid') + } + const { colorMode } = useColorMode(); // returns 'light' or 'dark' + const extraStyle = parseInlineStyle(style) + const combinedStyle:React.CSSProperties = { + wordBreak: 'break-word', + ...extraStyle + } + const header = title ? ( + <> + {title} + + ): null + + const code_block = click_ui === 'true' ? ( + + {typeof children === 'string' ? children : getCodeContent(children)} + + ): ( + + ) + const results = runnable ? ( + + ): null + + return ( +
+ + { header } + { code_block } + { results } + +
+ + + ) +} + +export default CodeViewer diff --git a/src/components/CodeViewer/types.tsx b/src/components/CodeViewer/types.tsx new file mode 100644 index 00000000000..af86dda1904 --- /dev/null +++ b/src/components/CodeViewer/types.tsx @@ -0,0 +1,87 @@ +export interface QueryResponse { + query?: string + status: number + query_id?: string + error?: any + response?: QueryResults + warning?: string | undefined +} + +export interface QueryResults { + meta: Column[] + data: Record[] + rows: number + exception?: string + statistics: QueryStatistics +} + +export interface QueryStatistics { + elapsed: number + rows_read: number + bytes_read: number +} + +export interface Column { + database?: string + table?: string + name: string + type: string +} + +export interface QueryParameter { + name: string + type?: string + textStartPos?: number + textEndPos?: number + value: string +} + +export enum ChartType { + Line = 'line', + Bar = 'bar', + HorizontalBar = 'horizontal bar', + Area = 'area', + Pie = 'pie', + Scatter = 'scatter', + HeatMap = 'heatmap', + CandleStick = 'candlestick' +} + +export interface YAxisConfig { + yaxis?: string + yaxis_min?: number + yaxis_max?: number +} + +export interface XAxisConfig { + xaxis?: string +} + +export interface SeriesConfig { + series?: string +} + +export interface StackableConfig { + stack?: boolean +} + +export type ChartConfig = StackableConfig & + SeriesConfig & + YAxisConfig & + XAxisConfig & { + title?: string + } + +export interface QueryResults { + meta: Column[] + data: Record[] + rows: number + exception?: string + statistics: QueryStatistics +} + +export interface QueryStatistics { + elapsed: number + rows_read: number + bytes_read: number +} diff --git a/src/components/CodeViewer/utils.ts b/src/components/CodeViewer/utils.ts new file mode 100644 index 00000000000..f8b72b2258b --- /dev/null +++ b/src/components/CodeViewer/utils.ts @@ -0,0 +1,58 @@ +import { filesize } from 'filesize' +import numeral from 'numeral' // @ts-ignore + +export function roundToDynamicPrecision(num: number) { + const validNum = Number(num) + if (isNaN(validNum)) return 0 + if (validNum === 0) return 0 + + const absNum = Math.abs(validNum) + const scale = Math.floor(Math.log10(absNum)) + + const numDecimals = (validNum.toString().split('.')[1] || '').length + + const basePrecision = scale >= 1 ? 0 : Math.max(0, -scale + 1) + const finalPrecision = Math.min( + 3, + Math.round((basePrecision + numDecimals) / 2) + ) + return Number(validNum.toFixed(finalPrecision)) +} + +export function nonNullType(type: string) { + type = type.trim() + if (type.startsWith('Nullable(')) { + return type.slice(9, -1) + } + return type +} + +export function roundByScale(num: number) { + const absNum = Math.abs(num) // incase - + const scale = absNum === 0 ? 0 : Math.floor(Math.log10(absNum)) // ensures we round to a value appropriate for scale + const precision = Math.max(0, -scale) + return Number(num.toFixed(precision)) +} + +export function base64Decode(base64: string): string { + // Convert URL-safe base64 back to regular base64 + const base64String = base64.replace(/-/g, '+').replace(/_/g, '/') + // Add padding if necessary + const paddedBase64String = base64String.padEnd( + base64String.length + ((4 - (base64String.length % 4)) % 4), + '=' + ) + const binaryString = atob(paddedBase64String) + const utf8Bytes = Uint8Array.from(binaryString, (char) => char.charCodeAt(0)) + const decodedText = new TextDecoder().decode(utf8Bytes) + return decodedText +} + +export function formatReadableRows(rows: number): string { + if (rows < 1000) return rows.toString() // Return as is if less than 1,000 + return numeral(rows).format('0.[0]a') // Converts to K, M, B with one decimal if needed +} + +export function formatBytes(bytes: number): string { + return filesize(bytes, { base: 2, standard: 'jedec', spacer: '' }) +} diff --git a/src/css/custom.scss b/src/css/custom.scss index 8990e7f408c..051c4ca5ce9 100644 --- a/src/css/custom.scss +++ b/src/css/custom.scss @@ -2,6 +2,10 @@ @use "default"; @use "home"; +@tailwind base; +@tailwind components; +@tailwind utilities; + /* KaTeX renders the input twice by default, once in HTML and one in MathML */ .katex-html { display: none; @@ -224,20 +228,59 @@ html { font-size: 16px; } +/* === Base styles === */ code { font-weight: bold; - background-color: var(--ifm-code-background) !important; + font-size: 0.95em; + border-radius: 0; + padding: 0.15em 0.35em; scrollbar-width: thin; scrollbar-color: rgb(99, 99, 99) rgb(41, 45, 62); - span { - font-style: normal !important; - } } -/* Style individual tokens */ +/* Normalize inner span styles */ +code span { + font-style: normal !important; +} + +/* === Theme-specific inline backgrounds === */ +html[data-theme='dark'] pre, +html[data-theme='dark'] pre code, +html[data-theme='dark'] code { + background: none !important; + background-color: #282828 !important; + box-shadow: none !important; + filter: none !important; + border: none !important; +} + +html[data-theme='light'] code { + background-color: #f5f5f5 !important; + color: #000 !important; +} + +/* === Block code (pre) === */ +pre { + padding: 1rem; + border-radius: 0 !important; +} + +html[data-theme='light'] pre, +html[data-theme='light'] pre code { + background-color: #f5f5f5 !important; + color: #000 !important; +} + +html[data-theme='dark'] pre, +html[data-theme='dark'] pre code { + background-color: #282828 !important; + color: var(--prism-color); +} + +/* === Prism token styles (Dark Theme) === */ code .token.keyword { - font-style: italic !important; /* Italicize keywords */ - color: rgb(86, 156, 214) !important; /* Blue for keywords */ + font-style: italic !important; + color: rgb(86, 156, 214) !important; } code .token.plain { @@ -245,26 +288,61 @@ code .token.plain { } code .token.punctuation { - color: rgb(199, 146, 234) !important; /* Match punctuation color */ + color: rgb(199, 146, 234) !important; } code .token.operator { - color: rgb(137, 221, 255) !important; /* Light blue for operators */ + color: rgb(137, 221, 255) !important; } code .token.string { - color: rgb(206, 145, 120) !important; /* Orange for strings */ + color: rgb(206, 145, 120) !important; } code .token.number { - color: rgb(181, 206, 168) !important; /* Green for numbers */ + color: rgb(181, 206, 168) !important; } code .token.function { - color: rgb(220, 220, 170) !important; /* Bright blue for functions */ + color: rgb(220, 220, 170) !important; +} + +/* === Prism token styles (Light Theme) === */ +html[data-theme='light'] code .token.keyword { + font-style: italic !important; + color: rgb(0, 92, 154) !important; +} + +html[data-theme='light'] code .token.plain { + color: rgb(36, 41, 46) !important; +} + +html[data-theme='light'] code .token.identifier { + color: rgb(0, 102, 204) !important; +} + +html[data-theme='light'] code .token.punctuation { + color: rgb(125, 86, 148) !important; +} + +html[data-theme='light'] code .token.operator { + color: rgb(0, 112, 138) !important; +} + +html[data-theme='light'] code .token.string { + color: rgb(163, 21, 21) !important; +} + +html[data-theme='light'] code .token.number { + color: rgb(34, 134, 58) !important; +} + +html[data-theme='light'] code .token.function { + color: rgb(88, 92, 63) !important; } -/* Ensure keywords are not italic */ +/* Ensure keywords are not italic if nested */ +html[data-theme='light'] code span.token.keyword, code span.token.keyword { font-style: normal !important; } diff --git a/src/theme/CodeBlock/Container/styles.module.css b/src/theme/CodeBlock/Container/styles.module.css index 61e40e38ad6..f3b27fb155e 100644 --- a/src/theme/CodeBlock/Container/styles.module.css +++ b/src/theme/CodeBlock/Container/styles.module.css @@ -1,11 +1,3 @@ .codeBlockContainer { - background: var(--prism-background-color); - color: var(--prism-color); - margin-bottom: var(--ifm-leading); - box-shadow: var(--ifm-global-shadow-lw); - border-radius: var(--ifm-code-border-radius); - --ifm-code-background: var(--prism-background-color); - --ifm-pre-color: var(--prism-color); - overflow-x: scroll; } diff --git a/src/theme/CodeBlock/index.js b/src/theme/CodeBlock/index.js index 69ec342e731..b8f76b0c582 100644 --- a/src/theme/CodeBlock/index.js +++ b/src/theme/CodeBlock/index.js @@ -1,6 +1,7 @@ import React, {useState, useRef, useCallback, useEffect} from 'react'; -import CodeBlock from '@theme-original/CodeBlock'; import styles from './styles.module.css'; +import CodeViewer from "../../components/CodeViewer"; + function countLines(text) { // Split the string by newline characters @@ -8,6 +9,30 @@ function countLines(text) { // Return the number of lines return lines.length; } + +function parseMetaString(meta = '') { + const result = {} + const implicit_settings = ['runnable', 'run', 'show_statistics', 'click_ui'] + + meta.split(' ').forEach((part) => { + if (!part) return + + const [rawKey, ...rawValueParts] = part.split('=') + const key = rawKey?.trim() + const rawValue = rawValueParts.join('=').trim() + + if (key && rawValue) { + // Remove surrounding single or double quotes if present + const cleanedValue = rawValue.replace(/^['"]|['"]$/g, '') + result[key] = cleanedValue + } else if (key && implicit_settings.includes(key)) { + result[key] = 'true' + } + }) + + return result +} + export default function CodeBlockWrapper(props) { const lineHeight = 18.85; const [isLoaded, setIsLoaded] = useState(false); @@ -47,9 +72,14 @@ export default function CodeBlockWrapper(props) { ); } - return ( - <> - - + + const settings = parseMetaString(props.metastring); + settings['language'] = props.className ? props.className.replace('language-', ''): 'txt'; + return ( + <> + + {props.children} + + ); } diff --git a/static/images/loading.svg b/static/images/loading.svg new file mode 100644 index 00000000000..821632dce66 --- /dev/null +++ b/static/images/loading.svg @@ -0,0 +1,7 @@ + + + + + diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 00000000000..42db927db97 --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,28 @@ +module.exports = { + darkMode: 'class', + plugins: [ + require('@tailwindcss/typography') + ], + content: [ + './src/**/*.{js,ts,jsx,tsx}', + ], + safelist: ['py-[6px]', 'py-[1px]'], + theme: { + screens: { + print: { + raw: 'print' + }, + sm: '640px', + 'sm-mid': '704px', + md: '768px', + 'md-mid': '896px', + lg: '1024px', + 'lg-mid': '1152px', + xl: '1280px', + 'xl-mid': '1408px', + '2xl': '1536px', + '3xl': '2200px' + }, + } + } + \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 8cb23fc1901..f7b73a567d5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -150,6 +150,11 @@ dependencies: "@algolia/client-common" "5.20.1" +"@alloc/quick-lru@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz#7bf68b20c0a350f936915fcae06f58e32007ce30" + integrity sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw== + "@ampproject/remapping@^2.2.0": version "2.3.0" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" @@ -1109,6 +1114,13 @@ core-js-pure "^3.30.2" regenerator-runtime "^0.14.0" +"@babel/runtime@^7.0.0", "@babel/runtime@^7.3.1": + version "7.27.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.27.0.tgz#fbee7cf97c709518ecc1f590984481d5460d4762" + integrity sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.3", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.25.9", "@babel/runtime@^7.8.4": version "7.26.10" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.10.tgz#a07b4d8fa27af131a633d7b3524db803eb4764c2" @@ -1200,6 +1212,47 @@ resolved "https://registry.yarnpkg.com/@chevrotain/utils/-/utils-11.0.3.tgz#e39999307b102cff3645ec4f5b3665f5297a2224" integrity sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ== +"@clickhouse/click-ui@^0.0.199": + version "0.0.199" + resolved "https://registry.yarnpkg.com/@clickhouse/click-ui/-/click-ui-0.0.199.tgz#4a9f6ddf38cc5d04c24e0775805425ae1b3e9e36" + integrity sha512-39I8JHibF4/qMZQV0KJvSovDDP5mC9HIapBCqrxB3+KgpnAhtIqZaNtiIA4tM7goFSJti6xwpzc6OL2LE3H9yA== + dependencies: + "@h6s/calendar" "^2.0.1" + "@radix-ui/react-accordion" "^1.1.2" + "@radix-ui/react-avatar" "^1.0.4" + "@radix-ui/react-checkbox" "^1.0.4" + "@radix-ui/react-context-menu" "^2.1.5" + "@radix-ui/react-dialog" "^1.0.5" + "@radix-ui/react-dismissable-layer" "^1.0.5" + "@radix-ui/react-dropdown-menu" "^2.0.6" + "@radix-ui/react-hover-card" "^1.0.7" + "@radix-ui/react-popover" "^1.0.7" + "@radix-ui/react-popper" "^1.1.3" + "@radix-ui/react-radio-group" "^1.1.3" + "@radix-ui/react-scroll-area" "^1.0.5" + "@radix-ui/react-separator" "^1.0.3" + "@radix-ui/react-tabs" "^1.0.4" + "@radix-ui/react-toast" "^1.1.5" + "@radix-ui/react-tooltip" "^1.0.7" + lodash "^4.17.21" + react-sortablejs "^6.1.4" + react-syntax-highlighter "^15.5.0" + react-virtualized-auto-sizer "^1.0.20" + react-window "^1.8.9" + sortablejs "^1.15.0" + +"@clickhouse/client-common@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@clickhouse/client-common/-/client-common-1.11.0.tgz#e66f74040228984dd1b7bda4749bc2d14cfa897d" + integrity sha512-O0xbwv7HiMXayokrf5dYIBpjBnYekcOXWz60T1cXLmiZ8vgrfNRCiOpybJkrMXKnw9D0mWCgPUu/rgMY7U1f4g== + +"@clickhouse/client-web@^1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@clickhouse/client-web/-/client-web-1.11.0.tgz#86601892f6b6a562ec4248f077e2964f2cf2fcb6" + integrity sha512-5N8ll85KRjVsxq2V0Y3gowcApwZA+YyIHIC7M5bFki8gtt3SihijH9cspdZBdBTFFzYPbiKS+zftKq7YQhoGxA== + dependencies: + "@clickhouse/client-common" "1.11.0" + "@colors/colors@1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" @@ -2342,6 +2395,38 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz#c8e119a30a7c8d60b9d2e22d2073722dde3b710b" integrity sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ== +"@floating-ui/core@^1.6.0": + version "1.6.9" + resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.6.9.tgz#64d1da251433019dafa091de9b2886ff35ec14e6" + integrity sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw== + dependencies: + "@floating-ui/utils" "^0.2.9" + +"@floating-ui/dom@^1.0.0": + version "1.6.13" + resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.6.13.tgz#a8a938532aea27a95121ec16e667a7cbe8c59e34" + integrity sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w== + dependencies: + "@floating-ui/core" "^1.6.0" + "@floating-ui/utils" "^0.2.9" + +"@floating-ui/react-dom@^2.0.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.1.2.tgz#a1349bbf6a0e5cb5ded55d023766f20a4d439a31" + integrity sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A== + dependencies: + "@floating-ui/dom" "^1.0.0" + +"@floating-ui/utils@^0.2.9": + version "0.2.9" + resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.9.tgz#50dea3616bc8191fb8e112283b49eaff03e78429" + integrity sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg== + +"@h6s/calendar@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@h6s/calendar/-/calendar-2.0.1.tgz#ce3f0c9311ca2a961b18c74be801891984ae219c" + integrity sha512-9q5ksdnUsLDeuSm5arXzkxHyS22u7yi5TtVr4DZgW514AjdL+76kx7d+ScX9sKusasRQVxrhM2LTVmd8A/u42Q== + "@hapi/hoek@^9.0.0", "@hapi/hoek@^9.3.0": version "9.3.0" resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb" @@ -2773,11 +2858,76 @@ resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.28.tgz#d45e01c4a56f143ee69c54dd6b12eade9e270a73" integrity sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw== +"@radix-ui/number@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/number/-/number-1.1.0.tgz#1e95610461a09cdf8bb05c152e76ca1278d5da46" + integrity sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ== + "@radix-ui/primitive@1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.1.1.tgz#fc169732d755c7fbad33ba8d0cd7fd10c90dc8e3" integrity sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA== +"@radix-ui/react-accordion@^1.1.2": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@radix-ui/react-accordion/-/react-accordion-1.2.3.tgz#7768a2d2daea18e5c09809f2c4b8097448ee2ff7" + integrity sha512-RIQ15mrcvqIkDARJeERSuXSry2N8uYnxkdDetpfmalT/+0ntOXLkFOsh9iwlAsCv+qcmhZjbdJogIm6WBa6c4A== + dependencies: + "@radix-ui/primitive" "1.1.1" + "@radix-ui/react-collapsible" "1.1.3" + "@radix-ui/react-collection" "1.1.2" + "@radix-ui/react-compose-refs" "1.1.1" + "@radix-ui/react-context" "1.1.1" + "@radix-ui/react-direction" "1.1.0" + "@radix-ui/react-id" "1.1.0" + "@radix-ui/react-primitive" "2.0.2" + "@radix-ui/react-use-controllable-state" "1.1.0" + +"@radix-ui/react-arrow@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-arrow/-/react-arrow-1.1.2.tgz#30c0d574d7bb10eed55cd7007b92d38b03c6b2ab" + integrity sha512-G+KcpzXHq24iH0uGG/pF8LyzpFJYGD4RfLjCIBfGdSLXvjLHST31RUiRVrupIBMvIppMgSzQ6l66iAxl03tdlg== + dependencies: + "@radix-ui/react-primitive" "2.0.2" + +"@radix-ui/react-avatar@^1.0.4": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@radix-ui/react-avatar/-/react-avatar-1.1.3.tgz#8de2bcebdc38fbe14e952ccacf05ba2cb426bd83" + integrity sha512-Paen00T4P8L8gd9bNsRMw7Cbaz85oxiv+hzomsRZgFm2byltPFDtfcoqlWJ8GyZlIBWgLssJlzLCnKU0G0302g== + dependencies: + "@radix-ui/react-context" "1.1.1" + "@radix-ui/react-primitive" "2.0.2" + "@radix-ui/react-use-callback-ref" "1.1.0" + "@radix-ui/react-use-layout-effect" "1.1.0" + +"@radix-ui/react-checkbox@^1.0.4": + version "1.1.4" + resolved "https://registry.yarnpkg.com/@radix-ui/react-checkbox/-/react-checkbox-1.1.4.tgz#d7f5cb0a82ca6bb4eb717b74e9b2b0cc73ecf7a0" + integrity sha512-wP0CPAHq+P5I4INKe3hJrIa1WoNqqrejzW+zoU0rOvo1b9gDEJJFl2rYfO1PYJUQCc2H1WZxIJmyv9BS8i5fLw== + dependencies: + "@radix-ui/primitive" "1.1.1" + "@radix-ui/react-compose-refs" "1.1.1" + "@radix-ui/react-context" "1.1.1" + "@radix-ui/react-presence" "1.1.2" + "@radix-ui/react-primitive" "2.0.2" + "@radix-ui/react-use-controllable-state" "1.1.0" + "@radix-ui/react-use-previous" "1.1.0" + "@radix-ui/react-use-size" "1.1.0" + +"@radix-ui/react-collapsible@1.1.3": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@radix-ui/react-collapsible/-/react-collapsible-1.1.3.tgz#522a5749646b7a393c9e9049165a9a6dfa924e91" + integrity sha512-jFSerheto1X03MUC0g6R7LedNW9EEGWdg9W1+MlpkMLwGkgkbUXLPBH/KIuWKXUoeYRVY11llqbTBDzuLg7qrw== + dependencies: + "@radix-ui/primitive" "1.1.1" + "@radix-ui/react-compose-refs" "1.1.1" + "@radix-ui/react-context" "1.1.1" + "@radix-ui/react-id" "1.1.0" + "@radix-ui/react-presence" "1.1.2" + "@radix-ui/react-primitive" "2.0.2" + "@radix-ui/react-use-controllable-state" "1.1.0" + "@radix-ui/react-use-layout-effect" "1.1.0" + "@radix-ui/react-collection@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@radix-ui/react-collection/-/react-collection-1.1.2.tgz#b45eccca1cb902fd078b237316bd9fa81e621e15" @@ -2793,17 +2943,49 @@ resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz#6f766faa975f8738269ebb8a23bad4f5a8d2faec" integrity sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw== +"@radix-ui/react-context-menu@^2.1.5": + version "2.2.6" + resolved "https://registry.yarnpkg.com/@radix-ui/react-context-menu/-/react-context-menu-2.2.6.tgz#752fd1d91f92bba287ef2b558770f4ca7d74523e" + integrity sha512-aUP99QZ3VU84NPsHeaFt4cQUNgJqFsLLOt/RbbWXszZ6MP0DpDyjkFZORr4RpAEx3sUBk+Kc8h13yGtC5Qw8dg== + dependencies: + "@radix-ui/primitive" "1.1.1" + "@radix-ui/react-context" "1.1.1" + "@radix-ui/react-menu" "2.1.6" + "@radix-ui/react-primitive" "2.0.2" + "@radix-ui/react-use-callback-ref" "1.1.0" + "@radix-ui/react-use-controllable-state" "1.1.0" + "@radix-ui/react-context@1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.1.1.tgz#82074aa83a472353bb22e86f11bcbd1c61c4c71a" integrity sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q== +"@radix-ui/react-dialog@^1.0.5": + version "1.1.6" + resolved "https://registry.yarnpkg.com/@radix-ui/react-dialog/-/react-dialog-1.1.6.tgz#65b4465e99ad900f28a98eed9a94bb21ec644bf7" + integrity sha512-/IVhJV5AceX620DUJ4uYVMymzsipdKBzo3edo+omeskCKGm9FRHM0ebIdbPnlQVJqyuHbuBltQUOG2mOTq2IYw== + dependencies: + "@radix-ui/primitive" "1.1.1" + "@radix-ui/react-compose-refs" "1.1.1" + "@radix-ui/react-context" "1.1.1" + "@radix-ui/react-dismissable-layer" "1.1.5" + "@radix-ui/react-focus-guards" "1.1.1" + "@radix-ui/react-focus-scope" "1.1.2" + "@radix-ui/react-id" "1.1.0" + "@radix-ui/react-portal" "1.1.4" + "@radix-ui/react-presence" "1.1.2" + "@radix-ui/react-primitive" "2.0.2" + "@radix-ui/react-slot" "1.1.2" + "@radix-ui/react-use-controllable-state" "1.1.0" + aria-hidden "^1.2.4" + react-remove-scroll "^2.6.3" + "@radix-ui/react-direction@1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-direction/-/react-direction-1.1.0.tgz#a7d39855f4d077adc2a1922f9c353c5977a09cdc" integrity sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg== -"@radix-ui/react-dismissable-layer@1.1.5": +"@radix-ui/react-dismissable-layer@1.1.5", "@radix-ui/react-dismissable-layer@^1.0.5": version "1.1.5" resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.5.tgz#96dde2be078c694a621e55e047406c58cd5fe774" integrity sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg== @@ -2814,6 +2996,48 @@ "@radix-ui/react-use-callback-ref" "1.1.0" "@radix-ui/react-use-escape-keydown" "1.1.0" +"@radix-ui/react-dropdown-menu@^2.0.6": + version "2.1.6" + resolved "https://registry.yarnpkg.com/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.6.tgz#b66b62648b378370aa3c38e5727fd3bc5b8792a3" + integrity sha512-no3X7V5fD487wab/ZYSHXq3H37u4NVeLDKI/Ks724X/eEFSSEFYZxWgsIlr1UBeEyDaM29HM5x9p1Nv8DuTYPA== + dependencies: + "@radix-ui/primitive" "1.1.1" + "@radix-ui/react-compose-refs" "1.1.1" + "@radix-ui/react-context" "1.1.1" + "@radix-ui/react-id" "1.1.0" + "@radix-ui/react-menu" "2.1.6" + "@radix-ui/react-primitive" "2.0.2" + "@radix-ui/react-use-controllable-state" "1.1.0" + +"@radix-ui/react-focus-guards@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.1.tgz#8635edd346304f8b42cae86b05912b61aef27afe" + integrity sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg== + +"@radix-ui/react-focus-scope@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.2.tgz#c0a4519cd95c772606a82fc5b96226cd7fdd2602" + integrity sha512-zxwE80FCU7lcXUGWkdt6XpTTCKPitG1XKOwViTxHVKIJhZl9MvIl2dVHeZENCWD9+EdWv05wlaEkRXUykU27RA== + dependencies: + "@radix-ui/react-compose-refs" "1.1.1" + "@radix-ui/react-primitive" "2.0.2" + "@radix-ui/react-use-callback-ref" "1.1.0" + +"@radix-ui/react-hover-card@^1.0.7": + version "1.1.6" + resolved "https://registry.yarnpkg.com/@radix-ui/react-hover-card/-/react-hover-card-1.1.6.tgz#94fb87c047e1bb3bfd70439cf7ee48165ea4efa5" + integrity sha512-E4ozl35jq0VRlrdc4dhHrNSV0JqBb4Jy73WAhBEK7JoYnQ83ED5r0Rb/XdVKw89ReAJN38N492BAPBZQ57VmqQ== + dependencies: + "@radix-ui/primitive" "1.1.1" + "@radix-ui/react-compose-refs" "1.1.1" + "@radix-ui/react-context" "1.1.1" + "@radix-ui/react-dismissable-layer" "1.1.5" + "@radix-ui/react-popper" "1.2.2" + "@radix-ui/react-portal" "1.1.4" + "@radix-ui/react-presence" "1.1.2" + "@radix-ui/react-primitive" "2.0.2" + "@radix-ui/react-use-controllable-state" "1.1.0" + "@radix-ui/react-id@1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-1.1.0.tgz#de47339656594ad722eb87f94a6b25f9cffae0ed" @@ -2821,6 +3045,30 @@ dependencies: "@radix-ui/react-use-layout-effect" "1.1.0" +"@radix-ui/react-menu@2.1.6": + version "2.1.6" + resolved "https://registry.yarnpkg.com/@radix-ui/react-menu/-/react-menu-2.1.6.tgz#05fb1ef3fd7545c8abe61178372902970cdec3ce" + integrity sha512-tBBb5CXDJW3t2mo9WlO7r6GTmWV0F0uzHZVFmlRmYpiSK1CDU5IKojP1pm7oknpBOrFZx/YgBRW9oorPO2S/Lg== + dependencies: + "@radix-ui/primitive" "1.1.1" + "@radix-ui/react-collection" "1.1.2" + "@radix-ui/react-compose-refs" "1.1.1" + "@radix-ui/react-context" "1.1.1" + "@radix-ui/react-direction" "1.1.0" + "@radix-ui/react-dismissable-layer" "1.1.5" + "@radix-ui/react-focus-guards" "1.1.1" + "@radix-ui/react-focus-scope" "1.1.2" + "@radix-ui/react-id" "1.1.0" + "@radix-ui/react-popper" "1.2.2" + "@radix-ui/react-portal" "1.1.4" + "@radix-ui/react-presence" "1.1.2" + "@radix-ui/react-primitive" "2.0.2" + "@radix-ui/react-roving-focus" "1.1.2" + "@radix-ui/react-slot" "1.1.2" + "@radix-ui/react-use-callback-ref" "1.1.0" + aria-hidden "^1.2.4" + react-remove-scroll "^2.6.3" + "@radix-ui/react-navigation-menu@^1.2.5": version "1.2.5" resolved "https://registry.yarnpkg.com/@radix-ui/react-navigation-menu/-/react-navigation-menu-1.2.5.tgz#c882e2067f1101a9a5f18e05b73b68b2e158a272" @@ -2841,6 +3089,51 @@ "@radix-ui/react-use-previous" "1.1.0" "@radix-ui/react-visually-hidden" "1.1.2" +"@radix-ui/react-popover@^1.0.7": + version "1.1.6" + resolved "https://registry.yarnpkg.com/@radix-ui/react-popover/-/react-popover-1.1.6.tgz#699634dbc7899429f657bb590d71fb3ca0904087" + integrity sha512-NQouW0x4/GnkFJ/pRqsIS3rM/k97VzKnVb2jB7Gq7VEGPy5g7uNV1ykySFt7eWSp3i2uSGFwaJcvIRJBAHmmFg== + dependencies: + "@radix-ui/primitive" "1.1.1" + "@radix-ui/react-compose-refs" "1.1.1" + "@radix-ui/react-context" "1.1.1" + "@radix-ui/react-dismissable-layer" "1.1.5" + "@radix-ui/react-focus-guards" "1.1.1" + "@radix-ui/react-focus-scope" "1.1.2" + "@radix-ui/react-id" "1.1.0" + "@radix-ui/react-popper" "1.2.2" + "@radix-ui/react-portal" "1.1.4" + "@radix-ui/react-presence" "1.1.2" + "@radix-ui/react-primitive" "2.0.2" + "@radix-ui/react-slot" "1.1.2" + "@radix-ui/react-use-controllable-state" "1.1.0" + aria-hidden "^1.2.4" + react-remove-scroll "^2.6.3" + +"@radix-ui/react-popper@1.2.2", "@radix-ui/react-popper@^1.1.3": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-popper/-/react-popper-1.2.2.tgz#d2e1ee5a9b24419c5936a1b7f6f472b7b412b029" + integrity sha512-Rvqc3nOpwseCyj/rgjlJDYAgyfw7OC1tTkKn2ivhaMGcYt8FSBlahHOZak2i3QwkRXUXgGgzeEe2RuqeEHuHgA== + dependencies: + "@floating-ui/react-dom" "^2.0.0" + "@radix-ui/react-arrow" "1.1.2" + "@radix-ui/react-compose-refs" "1.1.1" + "@radix-ui/react-context" "1.1.1" + "@radix-ui/react-primitive" "2.0.2" + "@radix-ui/react-use-callback-ref" "1.1.0" + "@radix-ui/react-use-layout-effect" "1.1.0" + "@radix-ui/react-use-rect" "1.1.0" + "@radix-ui/react-use-size" "1.1.0" + "@radix-ui/rect" "1.1.0" + +"@radix-ui/react-portal@1.1.4": + version "1.1.4" + resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.1.4.tgz#ff5401ff63c8a825c46eea96d3aef66074b8c0c8" + integrity sha512-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA== + dependencies: + "@radix-ui/react-primitive" "2.0.2" + "@radix-ui/react-use-layout-effect" "1.1.0" + "@radix-ui/react-presence@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.1.2.tgz#bb764ed8a9118b7ec4512da5ece306ded8703cdc" @@ -2856,6 +3149,59 @@ dependencies: "@radix-ui/react-slot" "1.1.2" +"@radix-ui/react-radio-group@^1.1.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@radix-ui/react-radio-group/-/react-radio-group-1.2.3.tgz#f60f58179cce716ccdb5c3d53a2eca97e4efd520" + integrity sha512-xtCsqt8Rp09FK50ItqEqTJ7Sxanz8EM8dnkVIhJrc/wkMMomSmXHvYbhv3E7Zx4oXh98aaLt9W679SUYXg4IDA== + dependencies: + "@radix-ui/primitive" "1.1.1" + "@radix-ui/react-compose-refs" "1.1.1" + "@radix-ui/react-context" "1.1.1" + "@radix-ui/react-direction" "1.1.0" + "@radix-ui/react-presence" "1.1.2" + "@radix-ui/react-primitive" "2.0.2" + "@radix-ui/react-roving-focus" "1.1.2" + "@radix-ui/react-use-controllable-state" "1.1.0" + "@radix-ui/react-use-previous" "1.1.0" + "@radix-ui/react-use-size" "1.1.0" + +"@radix-ui/react-roving-focus@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.2.tgz#815d051a54299114a68db6eb8d34c41a3c0a646f" + integrity sha512-zgMQWkNO169GtGqRvYrzb0Zf8NhMHS2DuEB/TiEmVnpr5OqPU3i8lfbxaAmC2J/KYuIQxyoQQ6DxepyXp61/xw== + dependencies: + "@radix-ui/primitive" "1.1.1" + "@radix-ui/react-collection" "1.1.2" + "@radix-ui/react-compose-refs" "1.1.1" + "@radix-ui/react-context" "1.1.1" + "@radix-ui/react-direction" "1.1.0" + "@radix-ui/react-id" "1.1.0" + "@radix-ui/react-primitive" "2.0.2" + "@radix-ui/react-use-callback-ref" "1.1.0" + "@radix-ui/react-use-controllable-state" "1.1.0" + +"@radix-ui/react-scroll-area@^1.0.5": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.3.tgz#6a9a7897add739ce84b517796ee345d495893d3f" + integrity sha512-l7+NNBfBYYJa9tNqVcP2AGvxdE3lmE6kFTBXdvHgUaZuy+4wGCL1Cl2AfaR7RKyimj7lZURGLwFO59k4eBnDJQ== + dependencies: + "@radix-ui/number" "1.1.0" + "@radix-ui/primitive" "1.1.1" + "@radix-ui/react-compose-refs" "1.1.1" + "@radix-ui/react-context" "1.1.1" + "@radix-ui/react-direction" "1.1.0" + "@radix-ui/react-presence" "1.1.2" + "@radix-ui/react-primitive" "2.0.2" + "@radix-ui/react-use-callback-ref" "1.1.0" + "@radix-ui/react-use-layout-effect" "1.1.0" + +"@radix-ui/react-separator@^1.0.3": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-separator/-/react-separator-1.1.2.tgz#24c5450db20f341f2b743ed4b07b907e18579216" + integrity sha512-oZfHcaAp2Y6KFBX6I5P1u7CQoy4lheCGiYj+pGFrHy8E/VNRb5E39TkTr3JrV520csPBTZjkuKFdEsjS5EUNKQ== + dependencies: + "@radix-ui/react-primitive" "2.0.2" + "@radix-ui/react-slot@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.1.2.tgz#daffff7b2bfe99ade63b5168407680b93c00e1c6" @@ -2863,6 +3209,56 @@ dependencies: "@radix-ui/react-compose-refs" "1.1.1" +"@radix-ui/react-tabs@^1.0.4": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@radix-ui/react-tabs/-/react-tabs-1.1.3.tgz#c47c8202dc676dea47676215863d2ef9b141c17a" + integrity sha512-9mFyI30cuRDImbmFF6O2KUJdgEOsGh9Vmx9x/Dh9tOhL7BngmQPQfwW4aejKm5OHpfWIdmeV6ySyuxoOGjtNng== + dependencies: + "@radix-ui/primitive" "1.1.1" + "@radix-ui/react-context" "1.1.1" + "@radix-ui/react-direction" "1.1.0" + "@radix-ui/react-id" "1.1.0" + "@radix-ui/react-presence" "1.1.2" + "@radix-ui/react-primitive" "2.0.2" + "@radix-ui/react-roving-focus" "1.1.2" + "@radix-ui/react-use-controllable-state" "1.1.0" + +"@radix-ui/react-toast@^1.1.5": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@radix-ui/react-toast/-/react-toast-1.2.6.tgz#f8d4bb2217851d221d700ac48fbe866b35023361" + integrity sha512-gN4dpuIVKEgpLn1z5FhzT9mYRUitbfZq9XqN/7kkBMUgFTzTG8x/KszWJugJXHcwxckY8xcKDZPz7kG3o6DsUA== + dependencies: + "@radix-ui/primitive" "1.1.1" + "@radix-ui/react-collection" "1.1.2" + "@radix-ui/react-compose-refs" "1.1.1" + "@radix-ui/react-context" "1.1.1" + "@radix-ui/react-dismissable-layer" "1.1.5" + "@radix-ui/react-portal" "1.1.4" + "@radix-ui/react-presence" "1.1.2" + "@radix-ui/react-primitive" "2.0.2" + "@radix-ui/react-use-callback-ref" "1.1.0" + "@radix-ui/react-use-controllable-state" "1.1.0" + "@radix-ui/react-use-layout-effect" "1.1.0" + "@radix-ui/react-visually-hidden" "1.1.2" + +"@radix-ui/react-tooltip@^1.0.7": + version "1.1.8" + resolved "https://registry.yarnpkg.com/@radix-ui/react-tooltip/-/react-tooltip-1.1.8.tgz#1aa2a575630fca2b2845b62f85056bb826bec456" + integrity sha512-YAA2cu48EkJZdAMHC0dqo9kialOcRStbtiY4nJPaht7Ptrhcvpo+eDChaM6BIs8kL6a8Z5l5poiqLnXcNduOkA== + dependencies: + "@radix-ui/primitive" "1.1.1" + "@radix-ui/react-compose-refs" "1.1.1" + "@radix-ui/react-context" "1.1.1" + "@radix-ui/react-dismissable-layer" "1.1.5" + "@radix-ui/react-id" "1.1.0" + "@radix-ui/react-popper" "1.2.2" + "@radix-ui/react-portal" "1.1.4" + "@radix-ui/react-presence" "1.1.2" + "@radix-ui/react-primitive" "2.0.2" + "@radix-ui/react-slot" "1.1.2" + "@radix-ui/react-use-controllable-state" "1.1.0" + "@radix-ui/react-visually-hidden" "1.1.2" + "@radix-ui/react-use-callback-ref@1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz#bce938ca413675bc937944b0d01ef6f4a6dc5bf1" @@ -2892,6 +3288,20 @@ resolved "https://registry.yarnpkg.com/@radix-ui/react-use-previous/-/react-use-previous-1.1.0.tgz#d4dd37b05520f1d996a384eb469320c2ada8377c" integrity sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og== +"@radix-ui/react-use-rect@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-rect/-/react-use-rect-1.1.0.tgz#13b25b913bd3e3987cc9b073a1a164bb1cf47b88" + integrity sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ== + dependencies: + "@radix-ui/rect" "1.1.0" + +"@radix-ui/react-use-size@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz#b4dba7fbd3882ee09e8d2a44a3eed3a7e555246b" + integrity sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw== + dependencies: + "@radix-ui/react-use-layout-effect" "1.1.0" + "@radix-ui/react-visually-hidden@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.2.tgz#8f6025507eb5d8b4b3215ebfd2c71a6632323a62" @@ -2899,6 +3309,11 @@ dependencies: "@radix-ui/react-primitive" "2.0.2" +"@radix-ui/rect@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/rect/-/rect-1.1.0.tgz#f817d1d3265ac5415dadc67edab30ae196696438" + integrity sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg== + "@rspack/binding-darwin-arm64@1.2.0-alpha.0": version "1.2.0-alpha.0" resolved "https://registry.yarnpkg.com/@rspack/binding-darwin-arm64/-/binding-darwin-arm64-1.2.0-alpha.0.tgz#234a0c42f6e89a2589f53ad8c44b2e85638bc77b" @@ -3287,6 +3702,109 @@ dependencies: defer-to-connect "^2.0.1" +"@tailwindcss/node@4.1.3": + version "4.1.3" + resolved "https://registry.yarnpkg.com/@tailwindcss/node/-/node-4.1.3.tgz#f290886582ce8eb1978853d07ca4da45f2d43fdb" + integrity sha512-H/6r6IPFJkCfBJZ2dKZiPJ7Ueb2wbL592+9bQEl2r73qbX6yGnmQVIfiUvDRB2YI0a3PWDrzUwkvQx1XW1bNkA== + dependencies: + enhanced-resolve "^5.18.1" + jiti "^2.4.2" + lightningcss "1.29.2" + tailwindcss "4.1.3" + +"@tailwindcss/oxide-android-arm64@4.1.3": + version "4.1.3" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.3.tgz#6c1834e7de84aa5544f4c8aacb380e00e019a11f" + integrity sha512-cxklKjtNLwFl3mDYw4XpEfBY+G8ssSg9ADL4Wm6//5woi3XGqlxFsnV5Zb6v07dxw1NvEX2uoqsxO/zWQsgR+g== + +"@tailwindcss/oxide-darwin-arm64@4.1.3": + version "4.1.3" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.3.tgz#ed3abd4a59f05a1ac58337b63d6fe82bb9903462" + integrity sha512-mqkf2tLR5VCrjBvuRDwzKNShRu99gCAVMkVsaEOFvv6cCjlEKXRecPu9DEnxp6STk5z+Vlbh1M5zY3nQCXMXhw== + +"@tailwindcss/oxide-darwin-x64@4.1.3": + version "4.1.3" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.3.tgz#d8a0786f4eae8203f8345fcf5b03f3284eee82af" + integrity sha512-7sGraGaWzXvCLyxrc7d+CCpUN3fYnkkcso3rCzwUmo/LteAl2ZGCDlGvDD8Y/1D3ngxT8KgDj1DSwOnNewKhmg== + +"@tailwindcss/oxide-freebsd-x64@4.1.3": + version "4.1.3" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.3.tgz#e76520e5341c3a44959901b8fefee78d4fc2f074" + integrity sha512-E2+PbcbzIReaAYZe997wb9rId246yDkCwAakllAWSGqe6VTg9hHle67hfH6ExjpV2LSK/siRzBUs5wVff3RW9w== + +"@tailwindcss/oxide-linux-arm-gnueabihf@4.1.3": + version "4.1.3" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.3.tgz#130c276e590b6ba621c443ac7faa702a709620c7" + integrity sha512-GvfbJ8wjSSjbLFFE3UYz4Eh8i4L6GiEYqCtA8j2Zd2oXriPuom/Ah/64pg/szWycQpzRnbDiJozoxFU2oJZyfg== + +"@tailwindcss/oxide-linux-arm64-gnu@4.1.3": + version "4.1.3" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.3.tgz#55e736a89d8547835026df3c5d6ce50467d71241" + integrity sha512-35UkuCWQTeG9BHcBQXndDOrpsnt3Pj9NVIB4CgNiKmpG8GnCNXeMczkUpOoqcOhO6Cc/mM2W7kaQ/MTEENDDXg== + +"@tailwindcss/oxide-linux-arm64-musl@4.1.3": + version "4.1.3" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.3.tgz#4ff54e4a40fede7a66e209b07f9b5da432d96678" + integrity sha512-dm18aQiML5QCj9DQo7wMbt1Z2tl3Giht54uVR87a84X8qRtuXxUqnKQkRDK5B4bCOmcZ580lF9YcoMkbDYTXHQ== + +"@tailwindcss/oxide-linux-x64-gnu@4.1.3": + version "4.1.3" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.3.tgz#66477a71fbaad552be882e8b7a56bb7519b47838" + integrity sha512-LMdTmGe/NPtGOaOfV2HuO7w07jI3cflPrVq5CXl+2O93DCewADK0uW1ORNAcfu2YxDUS035eY2W38TxrsqngxA== + +"@tailwindcss/oxide-linux-x64-musl@4.1.3": + version "4.1.3" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.3.tgz#403145ce43361e7d63886c878fdb09cd868920da" + integrity sha512-aalNWwIi54bbFEizwl1/XpmdDrOaCjRFQRgtbv9slWjmNPuJJTIKPHf5/XXDARc9CneW9FkSTqTbyvNecYAEGw== + +"@tailwindcss/oxide-win32-arm64-msvc@4.1.3": + version "4.1.3" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.3.tgz#0cc2bc59c228ce1d64156089af21acc4302081da" + integrity sha512-PEj7XR4OGTGoboTIAdXicKuWl4EQIjKHKuR+bFy9oYN7CFZo0eu74+70O4XuERX4yjqVZGAkCdglBODlgqcCXg== + +"@tailwindcss/oxide-win32-x64-msvc@4.1.3": + version "4.1.3" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.3.tgz#9bd5108b95b03dace8a2e5e738b1b2389f8a6d09" + integrity sha512-T8gfxECWDBENotpw3HR9SmNiHC9AOJdxs+woasRZ8Q/J4VHN0OMs7F+4yVNZ9EVN26Wv6mZbK0jv7eHYuLJLwA== + +"@tailwindcss/oxide@4.1.3": + version "4.1.3" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide/-/oxide-4.1.3.tgz#d01162137fcefe7d4c2a34500b9ed5c142388352" + integrity sha512-t16lpHCU7LBxDe/8dCj9ntyNpXaSTAgxWm1u2XQP5NiIu4KGSyrDJJRlK9hJ4U9yJxx0UKCVI67MJWFNll5mOQ== + optionalDependencies: + "@tailwindcss/oxide-android-arm64" "4.1.3" + "@tailwindcss/oxide-darwin-arm64" "4.1.3" + "@tailwindcss/oxide-darwin-x64" "4.1.3" + "@tailwindcss/oxide-freebsd-x64" "4.1.3" + "@tailwindcss/oxide-linux-arm-gnueabihf" "4.1.3" + "@tailwindcss/oxide-linux-arm64-gnu" "4.1.3" + "@tailwindcss/oxide-linux-arm64-musl" "4.1.3" + "@tailwindcss/oxide-linux-x64-gnu" "4.1.3" + "@tailwindcss/oxide-linux-x64-musl" "4.1.3" + "@tailwindcss/oxide-win32-arm64-msvc" "4.1.3" + "@tailwindcss/oxide-win32-x64-msvc" "4.1.3" + +"@tailwindcss/postcss@^4.1.3": + version "4.1.3" + resolved "https://registry.yarnpkg.com/@tailwindcss/postcss/-/postcss-4.1.3.tgz#82bf8b90c134f89f70d8d0293b5b14f234918faf" + integrity sha512-6s5nJODm98F++QT49qn8xJKHQRamhYHfMi3X7/ltxiSQ9dyRsaFSfFkfaMsanWzf+TMYQtbk8mt5f6cCVXJwfg== + dependencies: + "@alloc/quick-lru" "^5.2.0" + "@tailwindcss/node" "4.1.3" + "@tailwindcss/oxide" "4.1.3" + postcss "^8.4.41" + tailwindcss "4.1.3" + +"@tailwindcss/typography@^0.5.16": + version "0.5.16" + resolved "https://registry.yarnpkg.com/@tailwindcss/typography/-/typography-0.5.16.tgz#a926c8f44d5c439b2915e231cad80058850047c6" + integrity sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA== + dependencies: + lodash.castarray "^4.4.0" + lodash.isplainobject "^4.0.6" + lodash.merge "^4.6.2" + postcss-selector-parser "6.0.10" + "@trysound/sax@0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" @@ -3624,6 +4142,13 @@ resolved "https://registry.yarnpkg.com/@types/gtag.js/-/gtag.js-0.0.12.tgz#095122edca896689bdfcdd73b057e23064d23572" integrity sha512-YQV9bUsemkzG81Ea295/nF/5GijnD2Af7QhEofh7xu+kvCN6RdodgNwwGWXB5GMI3NoyvQo0odNctoH/qLMIpg== +"@types/hast@^2.0.0": + version "2.3.10" + resolved "https://registry.yarnpkg.com/@types/hast/-/hast-2.3.10.tgz#5c9d9e0b304bbb8879b857225c5ebab2d81d7643" + integrity sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw== + dependencies: + "@types/unist" "^2" + "@types/hast@^3.0.0": version "3.0.4" resolved "https://registry.yarnpkg.com/@types/hast/-/hast-3.0.4.tgz#1d6b39993b82cea6ad783945b0508c25903e15aa" @@ -3841,7 +4366,7 @@ resolved "https://registry.yarnpkg.com/@types/unist/-/unist-3.0.3.tgz#acaab0f919ce69cce629c2d4ed2eb4adc1b6c20c" integrity sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q== -"@types/unist@^2.0.0": +"@types/unist@^2", "@types/unist@^2.0.0": version "2.0.11" resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.11.tgz#11af57b127e32487774841f7a4e54eab166d03c4" integrity sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA== @@ -4150,6 +4675,11 @@ ansi-styles@^6.1.0, ansi-styles@^6.2.1: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== +any-base@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/any-base/-/any-base-1.1.0.tgz#ae101a62bc08a597b4c9ab5b7089d456630549fe" + integrity sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg== + anymatch@~3.1.2: version "3.1.3" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" @@ -4175,6 +4705,13 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== +aria-hidden@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/aria-hidden/-/aria-hidden-1.2.4.tgz#b78e383fdbc04d05762c78b4a25a501e736c4522" + integrity sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A== + dependencies: + tslib "^2.0.0" + array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" @@ -4586,16 +5123,31 @@ character-entities-html4@^2.0.0: resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-2.1.0.tgz#1f1adb940c971a4b22ba39ddca6b618dc6e56b2b" integrity sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA== +character-entities-legacy@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz#94bc1845dce70a5bb9d2ecc748725661293d8fc1" + integrity sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA== + character-entities-legacy@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz#76bc83a90738901d7bc223a9e93759fdd560125b" integrity sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ== +character-entities@^1.0.0: + version "1.2.4" + resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-1.2.4.tgz#e12c3939b7eaf4e5b15e7ad4c5e28e1d48c5b16b" + integrity sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw== + character-entities@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-2.0.2.tgz#2d09c2e72cd9523076ccb21157dff66ad43fcc22" integrity sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ== +character-reference-invalid@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz#083329cda0eae272ab3dbbf37e9a382c13af1560" + integrity sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg== + character-reference-invalid@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz#85c66b041e43b47210faf401278abf808ac45cb9" @@ -4699,6 +5251,11 @@ ci-info@^3.2.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== +classnames@2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e" + integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA== + clean-css@^5.2.2, clean-css@^5.3.2, clean-css@~5.3.2: version "5.3.3" resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.3.3.tgz#b330653cd3bd6b75009cc25c714cae7b93351ccd" @@ -4811,6 +5368,11 @@ combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" +comma-separated-tokens@^1.0.0: + version "1.0.8" + resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz#632b80b6117867a158f1080ad498b2fbe7e3f5ea" + integrity sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw== + comma-separated-tokens@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz#4e89c9458acb61bc8fef19f4529973b2392839ee" @@ -5676,6 +6238,11 @@ detect-libc@^2.0.0, detect-libc@^2.0.2, detect-libc@^2.0.3: resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700" integrity sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw== +detect-node-es@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/detect-node-es/-/detect-node-es-1.1.0.tgz#163acdf643330caa0b4cd7c21e7ee7755d6fa493" + integrity sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ== + detect-node@^2.0.4: version "2.1.0" resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" @@ -5835,6 +6402,22 @@ eastasianwidth@^0.2.0: resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== +echarts-for-react@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/echarts-for-react/-/echarts-for-react-3.0.2.tgz#ac5859157048a1066d4553e34b328abb24f2b7c1" + integrity sha512-DRwIiTzx8JfwPOVgGttDytBqdp5VzCSyMRIxubgU/g2n9y3VLUmF2FK7Icmg/sNVkv4+rktmrLN9w22U2yy3fA== + dependencies: + fast-deep-equal "^3.1.3" + size-sensor "^1.0.1" + +echarts@^5.6.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/echarts/-/echarts-5.6.0.tgz#2377874dca9fb50f104051c3553544752da3c9d6" + integrity sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA== + dependencies: + tslib "2.3.0" + zrender "5.6.1" + ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" @@ -5900,7 +6483,7 @@ end-of-stream@^1.1.0, end-of-stream@^1.4.1: dependencies: once "^1.4.0" -enhanced-resolve@^5.17.1: +enhanced-resolve@^5.17.1, enhanced-resolve@^5.18.1: version "5.18.1" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz#728ab082f8b7b6836de51f1637aab5d3b9568faf" integrity sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg== @@ -6298,6 +6881,13 @@ fastq@^1.6.0: dependencies: reusify "^1.0.4" +fault@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/fault/-/fault-1.0.4.tgz#eafcfc0a6d214fc94601e170df29954a4f842f13" + integrity sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA== + dependencies: + format "^0.2.0" + fault@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/fault/-/fault-2.0.1.tgz#d47ca9f37ca26e4bd38374a7c500b5a384755b6c" @@ -6342,6 +6932,11 @@ file-loader@^6.2.0: loader-utils "^2.0.0" schema-utils "^3.0.0" +filesize@^10.1.6: + version "10.1.6" + resolved "https://registry.yarnpkg.com/filesize/-/filesize-10.1.6.tgz#31194da825ac58689c0bce3948f33ce83aabd361" + integrity sha512-sJslQKU2uM33qH5nqewAwVB2QgR6w1aMNsYUp3aN5rMRyXEwJGmZvaWzeJFNTOXWlHQyBFCWrdj3fV/fsTOX8w== + filesize@^8.0.6: version "8.0.7" resolved "https://registry.yarnpkg.com/filesize/-/filesize-8.0.7.tgz#695e70d80f4e47012c132d57a059e80c6b580bd8" @@ -6548,6 +7143,11 @@ get-intrinsic@^1.2.4, get-intrinsic@^1.2.5, get-intrinsic@^1.2.6: hasown "^2.0.2" math-intrinsics "^1.1.0" +get-nonce@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/get-nonce/-/get-nonce-1.0.1.tgz#fdf3f0278073820d2ce9426c18f07481b1e0cdf3" + integrity sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q== + get-own-enumerable-property-symbols@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" @@ -6834,6 +7434,11 @@ hast-util-is-element@^3.0.0: dependencies: "@types/hast" "^3.0.0" +hast-util-parse-selector@^2.0.0: + version "2.2.5" + resolved "https://registry.yarnpkg.com/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz#d57c23f4da16ae3c63b3b6ca4616683313499c3a" + integrity sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ== + hast-util-parse-selector@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz#352879fa86e25616036037dd8931fb5f34cb4a27" @@ -6933,6 +7538,17 @@ hast-util-whitespace@^3.0.0: dependencies: "@types/hast" "^3.0.0" +hastscript@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-6.0.0.tgz#e8768d7eac56c3fdeac8a92830d58e811e5bf640" + integrity sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w== + dependencies: + "@types/hast" "^2.0.0" + comma-separated-tokens "^1.0.0" + hast-util-parse-selector "^2.0.0" + property-information "^5.0.0" + space-separated-tokens "^1.0.0" + hastscript@^9.0.0: version "9.0.0" resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-9.0.0.tgz#2b76b9aa3cba8bf6d5280869f6f6f7165c230763" @@ -6949,6 +7565,16 @@ he@^1.2.0: resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== +highlight.js@^10.4.1, highlight.js@~10.7.0: + version "10.7.3" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" + integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A== + +highlightjs-vue@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/highlightjs-vue/-/highlightjs-vue-1.0.0.tgz#fdfe97fbea6354e70ee44e3a955875e114db086d" + integrity sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA== + history@^4.9.0: version "4.10.1" resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3" @@ -7290,11 +7916,24 @@ ipaddr.js@^2.0.1: resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.2.0.tgz#d33fa7bac284f4de7af949638c9d68157c6b92e8" integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA== +is-alphabetical@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.4.tgz#9e7d6b94916be22153745d184c298cbf986a686d" + integrity sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg== + is-alphabetical@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-2.0.1.tgz#01072053ea7c1036df3c7d19a6daaec7f19e789b" integrity sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ== +is-alphanumerical@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz#7eb9a2431f855f6b1ef1a78e326df515696c4dbf" + integrity sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A== + dependencies: + is-alphabetical "^1.0.0" + is-decimal "^1.0.0" + is-alphanumerical@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz#7c03fbe96e3e931113e57f964b0a368cc2dfd875" @@ -7334,6 +7973,11 @@ is-core-module@^2.16.0: dependencies: hasown "^2.0.2" +is-decimal@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.4.tgz#65a3a5958a1c5b63a706e1b333d7cd9f630d3fa5" + integrity sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw== + is-decimal@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-2.0.1.tgz#9469d2dc190d0214fd87d78b78caecc0cc14eef7" @@ -7366,6 +8010,11 @@ is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-hexadecimal@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" + integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw== + is-hexadecimal@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz#86b5bf668fca307498d319dfc03289d781a90027" @@ -7557,6 +8206,11 @@ jiti@^1.20.0: resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.7.tgz#9dd81043424a3d28458b193d965f0d18a2300ba9" integrity sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A== +jiti@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/jiti/-/jiti-2.4.2.tgz#d19b7732ebb6116b06e2038da74a55366faef560" + integrity sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A== + joi@^17.9.2: version "17.13.3" resolved "https://registry.yarnpkg.com/joi/-/joi-17.13.3.tgz#0f5cc1169c999b30d344366d384b12d92558bcec" @@ -7774,7 +8428,7 @@ lightningcss-win32-x64-msvc@1.29.2: resolved "https://registry.yarnpkg.com/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.29.2.tgz#ddefaa099a39b725b2f5bbdcb9fc718435cc9797" integrity sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA== -lightningcss@^1.27.0: +lightningcss@1.29.2, lightningcss@^1.27.0: version "1.29.2" resolved "https://registry.yarnpkg.com/lightningcss/-/lightningcss-1.29.2.tgz#f5f0fd6e63292a232697e6fe709da5b47624def3" integrity sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA== @@ -7863,6 +8517,11 @@ lodash-es@4.17.21, lodash-es@^4.17.21: resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== +lodash.castarray@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.castarray/-/lodash.castarray-4.4.0.tgz#c02513515e309daddd4c24c60cfddcf5976d9115" + integrity sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q== + lodash.clonedeep@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" @@ -7873,11 +8532,21 @@ lodash.debounce@^4.0.8: resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== + lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" @@ -7920,6 +8589,14 @@ lowercase-keys@^3.0.0: resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-3.0.0.tgz#c5e7d442e37ead247ae9db117a9d0a467c89d4f2" integrity sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ== +lowlight@^1.17.0: + version "1.20.0" + resolved "https://registry.yarnpkg.com/lowlight/-/lowlight-1.20.0.tgz#ddb197d33462ad0d93bf19d17b6c301aa3941888" + integrity sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw== + dependencies: + fault "^1.0.0" + highlight.js "~10.7.0" + lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -8266,6 +8943,11 @@ memfs@^3.1.2, memfs@^3.4.3: dependencies: fs-monkey "^1.0.4" +"memoize-one@>=3.1.1 <6": + version "5.2.1" + resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e" + integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q== + merge-descriptors@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" @@ -9005,6 +9687,11 @@ null-loader@^4.0.1: loader-utils "^2.0.0" schema-utils "^3.0.0" +numeral@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/numeral/-/numeral-2.0.6.tgz#4ad080936d443c2561aed9f2197efffe25f4e506" + integrity sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA== + object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -9218,6 +9905,18 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" +parse-entities@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-2.0.0.tgz#53c6eb5b9314a1f4ec99fa0fdf7ce01ecda0cbe8" + integrity sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ== + dependencies: + character-entities "^1.0.0" + character-entities-legacy "^1.0.0" + character-reference-invalid "^1.0.0" + is-alphanumerical "^1.0.0" + is-decimal "^1.0.0" + is-hexadecimal "^1.0.0" + parse-entities@^4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-4.0.2.tgz#61d46f5ed28e4ee62e9ddc43d6b010188443f159" @@ -9912,6 +10611,14 @@ postcss-selector-not@^8.0.1: dependencies: postcss-selector-parser "^7.0.0" +postcss-selector-parser@6.0.10: + version "6.0.10" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz#79b61e2c0d1bfc2602d549e11d0876256f8df88d" + integrity sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + postcss-selector-parser@^6.0.11, postcss-selector-parser@^6.0.16: version "6.1.2" resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz#27ecb41fb0e3b6ba7a1ec84fff347f734c7929de" @@ -9969,6 +10676,15 @@ postcss@^8.4.21, postcss@^8.4.24, postcss@^8.4.26, postcss@^8.4.33, postcss@^8.4 picocolors "^1.1.1" source-map-js "^1.2.1" +postcss@^8.4.41, postcss@^8.5.3: + version "8.5.3" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.3.tgz#1463b6f1c7fb16fe258736cba29a2de35237eafb" + integrity sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A== + dependencies: + nanoid "^3.3.8" + picocolors "^1.1.1" + source-map-js "^1.2.1" + prebuild-install@^7.1.1: version "7.1.3" resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.3.tgz#d630abad2b147443f20a212917beae68b8092eec" @@ -10008,11 +10724,16 @@ prism-react-renderer@^2.3.0, prism-react-renderer@^2.4.1: "@types/prismjs" "^1.26.0" clsx "^2.0.0" -prismjs@^1.29.0: +prismjs@^1.27.0, prismjs@^1.29.0: version "1.30.0" resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.30.0.tgz#d9709969d9d4e16403f6f348c63553b19f0975a9" integrity sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw== +prismjs@~1.27.0: + version "1.27.0" + resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.27.0.tgz#bb6ee3138a0b438a3653dd4d6ce0cc6510a45057" + integrity sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA== + process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -10035,6 +10756,13 @@ prop-types@^15.0.0, prop-types@^15.6.2, prop-types@^15.7.2: object-assign "^4.1.1" react-is "^16.13.1" +property-information@^5.0.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/property-information/-/property-information-5.6.0.tgz#61675545fb23002f245c6540ec46077d4da3ed69" + integrity sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA== + dependencies: + xtend "^4.0.0" + property-information@^6.0.0: version "6.5.0" resolved "https://registry.yarnpkg.com/property-information/-/property-information-6.5.0.tgz#6212fbb52ba757e92ef4fb9d657563b933b7ffec" @@ -10237,6 +10965,25 @@ react-medium-image-zoom@^5.2.14: resolved "https://registry.yarnpkg.com/react-medium-image-zoom/-/react-medium-image-zoom-5.2.14.tgz#87032d079fce4a21a17770d6709f739872580906" integrity sha512-nfTVYcAUnBzXQpPDcZL+cG/e6UceYUIG+zDcnemL7jtAqbJjVVkA85RgneGtJeni12dTyiRPZVM6Szkmwd/o8w== +react-remove-scroll-bar@^2.3.7: + version "2.3.8" + resolved "https://registry.yarnpkg.com/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz#99c20f908ee467b385b68a3469b4a3e750012223" + integrity sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q== + dependencies: + react-style-singleton "^2.2.2" + tslib "^2.0.0" + +react-remove-scroll@^2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/react-remove-scroll/-/react-remove-scroll-2.6.3.tgz#df02cde56d5f2731e058531f8ffd7f9adec91ac2" + integrity sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ== + dependencies: + react-remove-scroll-bar "^2.3.7" + react-style-singleton "^2.2.3" + tslib "^2.1.0" + use-callback-ref "^1.3.3" + use-sidecar "^1.1.3" + react-router-config@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/react-router-config/-/react-router-config-5.1.1.tgz#0f4263d1a80c6b2dc7b9c1902c9526478194a988" @@ -10272,6 +11019,39 @@ react-router@5.3.4, react-router@^5.3.4: tiny-invariant "^1.0.2" tiny-warning "^1.0.0" +react-sortablejs@^6.1.4: + version "6.1.4" + resolved "https://registry.yarnpkg.com/react-sortablejs/-/react-sortablejs-6.1.4.tgz#420ebfab602bbd935035dec24a04c8b3b836dbbf" + integrity sha512-fc7cBosfhnbh53Mbm6a45W+F735jwZ1UFIYSrIqcO/gRIFoDyZeMtgKlpV4DdyQfbCzdh5LoALLTDRxhMpTyXQ== + dependencies: + classnames "2.3.1" + tiny-invariant "1.2.0" + +react-style-singleton@^2.2.2, react-style-singleton@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/react-style-singleton/-/react-style-singleton-2.2.3.tgz#4265608be69a4d70cfe3047f2c6c88b2c3ace388" + integrity sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ== + dependencies: + get-nonce "^1.0.0" + tslib "^2.0.0" + +react-syntax-highlighter@^15.5.0: + version "15.6.1" + resolved "https://registry.yarnpkg.com/react-syntax-highlighter/-/react-syntax-highlighter-15.6.1.tgz#fa567cb0a9f96be7bbccf2c13a3c4b5657d9543e" + integrity sha512-OqJ2/vL7lEeV5zTJyG7kmARppUjiB9h9udl4qHQjjgEos66z00Ia0OckwYfRxCSFrW8RJIBnsBwQsHZbVPspqg== + dependencies: + "@babel/runtime" "^7.3.1" + highlight.js "^10.4.1" + highlightjs-vue "^1.0.0" + lowlight "^1.17.0" + prismjs "^1.27.0" + refractor "^3.6.0" + +react-virtualized-auto-sizer@^1.0.20: + version "1.0.26" + resolved "https://registry.yarnpkg.com/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.26.tgz#e9470ef6a778dc4f1d5fd76305fa2d8b610c357a" + integrity sha512-CblNyiNVw2o+hsa5/49NH2ogGxZ+t+3aweRvNSq7TVjDIlwk7ir4lencEg5HxHeSzwNarSkNkiu0qJSOXtxm5A== + react-waypoint@^10.3.0: version "10.3.0" resolved "https://registry.yarnpkg.com/react-waypoint/-/react-waypoint-10.3.0.tgz#fcc60e86c6c9ad2174fa58d066dc6ae54e3df71d" @@ -10282,6 +11062,14 @@ react-waypoint@^10.3.0: prop-types "^15.0.0" react-is "^17.0.1 || ^18.0.0" +react-window@^1.8.9: + version "1.8.11" + resolved "https://registry.yarnpkg.com/react-window/-/react-window-1.8.11.tgz#a857b48fa85bd77042d59cc460964ff2e0648525" + integrity sha512-+SRbUVT2scadgFSWx+R1P754xHPEqvcfSfVX10QYg6POOz+WNgkN48pS+BtZNIMGiL1HYrSEiCkwsMS15QogEQ== + dependencies: + "@babel/runtime" "^7.0.0" + memoize-one ">=3.1.1 <6" + react@^18.2.0: version "18.3.1" resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891" @@ -10382,6 +11170,15 @@ recursive-readdir@^2.2.2: dependencies: minimatch "^3.0.5" +refractor@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/refractor/-/refractor-3.6.0.tgz#ac318f5a0715ead790fcfb0c71f4dd83d977935a" + integrity sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA== + dependencies: + hastscript "^6.0.0" + parse-entities "^2.0.0" + prismjs "~1.27.0" + regenerate-unicode-properties@^10.2.0: version "10.2.0" resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz#626e39df8c372338ea9b8028d1f99dc3fd9c3db0" @@ -11001,6 +11798,14 @@ shelljs@^0.8.5: interpret "^1.0.0" rechoir "^0.6.2" +short-uuid@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/short-uuid/-/short-uuid-5.2.0.tgz#49378f8c5335a603bc801c279ae521c5d22532dc" + integrity sha512-296/Nzi4DmANh93iYBwT4NoYRJuHnKEzefrkSagQbTH/A6NTaB68hSPDjm5IlbI5dx9FXdmtqPcj6N5H+CPm6w== + dependencies: + any-base "^1.1.0" + uuid "^9.0.1" + side-channel-list@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" @@ -11096,6 +11901,11 @@ sitemap@^7.1.1: arg "^5.0.0" sax "^1.2.4" +size-sensor@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/size-sensor/-/size-sensor-1.0.2.tgz#b8f8da029683cf2b4e22f12bf8b8f0a1145e8471" + integrity sha512-2NCmWxY7A9pYKGXNBfteo4hy14gWu47rg5692peVMst6lQLPKrVjhY+UTEsPI5ceFRJSl3gVgMYaUi/hKuaiKw== + skin-tone@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/skin-tone/-/skin-tone-2.0.0.tgz#4e3933ab45c0d4f4f781745d64b9f4c208e41237" @@ -11140,6 +11950,11 @@ sort-css-media-queries@2.2.0: resolved "https://registry.yarnpkg.com/sort-css-media-queries/-/sort-css-media-queries-2.2.0.tgz#aa33cf4a08e0225059448b6c40eddbf9f1c8334c" integrity sha512-0xtkGhWCC9MGt/EzgnvbbbKhqWjl1+/rncmhTh5qCpbYguXh6S/qwePfv/JQ8jePXXmqingylxoC49pCkSPIbA== +sortablejs@^1.15.0: + version "1.15.6" + resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.15.6.tgz#ff93699493f5b8ab8d828f933227b4988df1d393" + integrity sha512-aNfiuwMEpfBM/CN6LY0ibyhxPfPbyFeBTYJKCvzkJ2GkUpazIt3H+QIPAMHwqQ7tMKaHz1Qj+rJJCqljnf4p3A== + source-list-map@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" @@ -11168,6 +11983,11 @@ source-map@^0.7.0: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== +space-separated-tokens@^1.0.0: + version "1.1.5" + resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz#85f32c3d10d9682007e917414ddc5c26d1aa6899" + integrity sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA== + space-separated-tokens@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz#1ecd9d2350a3844572c3f4a312bceb018348859f" @@ -11404,6 +12224,11 @@ swc-loader@^0.2.6: dependencies: "@swc/counter" "^0.1.3" +tailwindcss@4.1.3, tailwindcss@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-4.1.3.tgz#f5a6b4451295c06e213013697f7193be1630fa46" + integrity sha512-2Q+rw9vy1WFXu5cIxlvsabCwhU2qUwodGq03ODhLJ0jW4ek5BUtoCsnLB0qG+m8AHgEsSJcJGDSDe06FXlP74g== + tapable@^1.0.0: version "1.1.3" resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" @@ -11493,6 +12318,11 @@ thunky@^1.0.2: resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== +tiny-invariant@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.2.0.tgz#a1141f86b672a9148c72e978a19a73b9b94a15a9" + integrity sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg== + tiny-invariant@^1.0.2: version "1.3.3" resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.3.tgz#46680b7a873a0d5d10005995eb90a70d74d60127" @@ -11545,7 +12375,12 @@ ts-dedent@^2.2.0: resolved "https://registry.yarnpkg.com/ts-dedent/-/ts-dedent-2.2.0.tgz#39e4bd297cd036292ae2394eb3412be63f563bb5" integrity sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ== -tslib@^2.0.3, tslib@^2.4.0, tslib@^2.6.0: +tslib@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e" + integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg== + +tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.4.0, tslib@^2.6.0: version "2.8.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== @@ -11847,6 +12682,21 @@ url-loader@^4.1.1: mime-types "^2.1.27" schema-utils "^3.0.0" +use-callback-ref@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/use-callback-ref/-/use-callback-ref-1.3.3.tgz#98d9fab067075841c5b2c6852090d5d0feabe2bf" + integrity sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg== + dependencies: + tslib "^2.0.0" + +use-sidecar@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/use-sidecar/-/use-sidecar-1.1.3.tgz#10e7fd897d130b896e2c546c63a5e8233d00efdb" + integrity sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ== + dependencies: + detect-node-es "^1.1.0" + tslib "^2.0.0" + util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -12238,6 +13088,11 @@ xml-js@^1.6.11: dependencies: sax "^1.2.4" +xtend@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + yallist@^3.0.2: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" @@ -12263,6 +13118,13 @@ yocto-queue@^1.0.0: resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.1.1.tgz#fef65ce3ac9f8a32ceac5a634f74e17e5b232110" integrity sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g== +zrender@5.6.1: + version "5.6.1" + resolved "https://registry.yarnpkg.com/zrender/-/zrender-5.6.1.tgz#e08d57ecf4acac708c4fcb7481eb201df7f10a6b" + integrity sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag== + dependencies: + tslib "2.3.0" + zwitch@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-2.0.4.tgz#c827d4b0acb76fc3e685a4c6ec2902d51070e9d7"