@tanstack/pacer-devtools
Advanced tools
| import { createComponent, escape, ssr, ssrAttribute, ssrStyleProperty } from "solid-js/web"; | ||
| import { Header, HeaderLogo, JsonTree, MainPanel, ThemeContextProvider, useTheme } from "@tanstack/devtools-ui"; | ||
| import { createStore } from "solid-js/store"; | ||
| import { For, createContext, createEffect, createMemo, createSignal, onCleanup, onMount, useContext } from "solid-js"; | ||
| import { pacerEventClient } from "@tanstack/pacer/event-client"; | ||
| import * as goober from "goober"; | ||
| import { useStore } from "@tanstack/solid-store"; | ||
| import clsx from "clsx"; | ||
| import dayjs from "dayjs"; | ||
| import relativeTime from "dayjs/plugin/relativeTime.js"; | ||
| //#region src/PacerContextProvider.tsx | ||
| const initialPacerDevtoolsStore = { | ||
| asyncBatchers: [], | ||
| asyncDebouncers: [], | ||
| asyncQueuers: [], | ||
| asyncRateLimiters: [], | ||
| asyncThrottlers: [], | ||
| batchers: [], | ||
| debouncers: [], | ||
| queuers: [], | ||
| rateLimiters: [], | ||
| throttlers: [], | ||
| lastUpdatedByKey: {} | ||
| }; | ||
| const PacerDevtoolsContext = createContext([initialPacerDevtoolsStore, () => {}]); | ||
| const updateOrAddToArray = (oldArray, newItem) => { | ||
| const index = oldArray.findIndex((item) => item.key === newItem.key); | ||
| if (index !== -1) return oldArray.map((item, i) => i === index ? newItem : item); | ||
| return [...oldArray, newItem]; | ||
| }; | ||
| function PacerContextProvider(props) { | ||
| const [store, setStore] = createStore(initialPacerDevtoolsStore); | ||
| createEffect(() => { | ||
| onCleanup(pacerEventClient.onAllPluginEvents((_e) => { | ||
| const e = _e; | ||
| switch (e.type) { | ||
| case "pacer:AsyncBatcher": | ||
| setStore({ | ||
| asyncBatchers: updateOrAddToArray(store.asyncBatchers, e.payload), | ||
| lastUpdatedByKey: { | ||
| ...store.lastUpdatedByKey, | ||
| [e.payload.key]: Date.now() | ||
| } | ||
| }); | ||
| break; | ||
| case "pacer:AsyncDebouncer": | ||
| setStore({ | ||
| asyncDebouncers: updateOrAddToArray(store.asyncDebouncers, e.payload), | ||
| lastUpdatedByKey: { | ||
| ...store.lastUpdatedByKey, | ||
| [e.payload.key]: Date.now() | ||
| } | ||
| }); | ||
| break; | ||
| case "pacer:AsyncQueuer": | ||
| setStore({ | ||
| asyncQueuers: updateOrAddToArray(store.asyncQueuers, e.payload), | ||
| lastUpdatedByKey: { | ||
| ...store.lastUpdatedByKey, | ||
| [e.payload.key]: Date.now() | ||
| } | ||
| }); | ||
| break; | ||
| case "pacer:AsyncRateLimiter": | ||
| setStore({ | ||
| asyncRateLimiters: updateOrAddToArray(store.asyncRateLimiters, e.payload), | ||
| lastUpdatedByKey: { | ||
| ...store.lastUpdatedByKey, | ||
| [e.payload.key]: Date.now() | ||
| } | ||
| }); | ||
| break; | ||
| case "pacer:AsyncThrottler": | ||
| setStore({ | ||
| asyncThrottlers: updateOrAddToArray(store.asyncThrottlers, e.payload), | ||
| lastUpdatedByKey: { | ||
| ...store.lastUpdatedByKey, | ||
| [e.payload.key]: Date.now() | ||
| } | ||
| }); | ||
| break; | ||
| case "pacer:Batcher": | ||
| setStore({ | ||
| batchers: updateOrAddToArray(store.batchers, e.payload), | ||
| lastUpdatedByKey: { | ||
| ...store.lastUpdatedByKey, | ||
| [e.payload.key]: Date.now() | ||
| } | ||
| }); | ||
| break; | ||
| case "pacer:Debouncer": | ||
| setStore({ | ||
| debouncers: updateOrAddToArray(store.debouncers, e.payload), | ||
| lastUpdatedByKey: { | ||
| ...store.lastUpdatedByKey, | ||
| [e.payload.key]: Date.now() | ||
| } | ||
| }); | ||
| break; | ||
| case "pacer:Queuer": | ||
| setStore({ | ||
| queuers: updateOrAddToArray(store.queuers, e.payload), | ||
| lastUpdatedByKey: { | ||
| ...store.lastUpdatedByKey, | ||
| [e.payload.key]: Date.now() | ||
| } | ||
| }); | ||
| break; | ||
| case "pacer:RateLimiter": | ||
| setStore({ | ||
| rateLimiters: updateOrAddToArray(store.rateLimiters, e.payload), | ||
| lastUpdatedByKey: { | ||
| ...store.lastUpdatedByKey, | ||
| [e.payload.key]: Date.now() | ||
| } | ||
| }); | ||
| break; | ||
| case "pacer:Throttler": | ||
| setStore({ | ||
| throttlers: updateOrAddToArray(store.throttlers, e.payload), | ||
| lastUpdatedByKey: { | ||
| ...store.lastUpdatedByKey, | ||
| [e.payload.key]: Date.now() | ||
| } | ||
| }); | ||
| break; | ||
| } | ||
| })); | ||
| }); | ||
| return createComponent(PacerDevtoolsContext.Provider, { | ||
| value: [store, setStore], | ||
| get children() { | ||
| return props.children; | ||
| } | ||
| }); | ||
| } | ||
| const usePacerDevtoolsContext = () => { | ||
| return useContext(PacerDevtoolsContext); | ||
| }; | ||
| const usePacerDevtoolsState = () => { | ||
| const [state] = usePacerDevtoolsContext(); | ||
| return state; | ||
| }; | ||
| //#endregion | ||
| //#region src/styles/tokens.ts | ||
| const tokens = { | ||
| colors: { | ||
| inherit: "inherit", | ||
| current: "currentColor", | ||
| transparent: "transparent", | ||
| black: "#000000", | ||
| white: "#ffffff", | ||
| neutral: { | ||
| 50: "#f9fafb", | ||
| 100: "#f2f4f7", | ||
| 200: "#eaecf0", | ||
| 300: "#d0d5dd", | ||
| 400: "#98a2b3", | ||
| 500: "#667085", | ||
| 600: "#475467", | ||
| 700: "#344054", | ||
| 800: "#1d2939", | ||
| 900: "#101828" | ||
| }, | ||
| darkGray: { | ||
| 50: "#525c7a", | ||
| 100: "#49536e", | ||
| 200: "#414962", | ||
| 300: "#394056", | ||
| 400: "#313749", | ||
| 500: "#292e3d", | ||
| 600: "#212530", | ||
| 700: "#191c24", | ||
| 800: "#111318", | ||
| 900: "#0b0d10" | ||
| }, | ||
| gray: { | ||
| 50: "#f9fafb", | ||
| 100: "#f2f4f7", | ||
| 200: "#eaecf0", | ||
| 300: "#d0d5dd", | ||
| 400: "#98a2b3", | ||
| 500: "#667085", | ||
| 600: "#475467", | ||
| 700: "#344054", | ||
| 800: "#1d2939", | ||
| 900: "#101828" | ||
| }, | ||
| blue: { | ||
| 25: "#F5FAFF", | ||
| 50: "#EFF8FF", | ||
| 100: "#D1E9FF", | ||
| 200: "#B2DDFF", | ||
| 300: "#84CAFF", | ||
| 400: "#53B1FD", | ||
| 500: "#2E90FA", | ||
| 600: "#1570EF", | ||
| 700: "#175CD3", | ||
| 800: "#1849A9", | ||
| 900: "#194185" | ||
| }, | ||
| green: { | ||
| 25: "#F6FEF9", | ||
| 50: "#ECFDF3", | ||
| 100: "#D1FADF", | ||
| 200: "#A6F4C5", | ||
| 300: "#6CE9A6", | ||
| 400: "#32D583", | ||
| 500: "#12B76A", | ||
| 600: "#039855", | ||
| 700: "#027A48", | ||
| 800: "#05603A", | ||
| 900: "#054F31" | ||
| }, | ||
| red: { | ||
| 50: "#fef2f2", | ||
| 100: "#fee2e2", | ||
| 200: "#fecaca", | ||
| 300: "#fca5a5", | ||
| 400: "#f87171", | ||
| 500: "#ef4444", | ||
| 600: "#dc2626", | ||
| 700: "#b91c1c", | ||
| 800: "#991b1b", | ||
| 900: "#7f1d1d", | ||
| 950: "#450a0a" | ||
| }, | ||
| yellow: { | ||
| 25: "#FFFCF5", | ||
| 50: "#FFFAEB", | ||
| 100: "#FEF0C7", | ||
| 200: "#FEDF89", | ||
| 300: "#FEC84B", | ||
| 400: "#FDB022", | ||
| 500: "#F79009", | ||
| 600: "#DC6803", | ||
| 700: "#B54708", | ||
| 800: "#93370D", | ||
| 900: "#7A2E0E" | ||
| }, | ||
| purple: { | ||
| 25: "#FAFAFF", | ||
| 50: "#F4F3FF", | ||
| 100: "#EBE9FE", | ||
| 200: "#D9D6FE", | ||
| 300: "#BDB4FE", | ||
| 400: "#9B8AFB", | ||
| 500: "#7A5AF8", | ||
| 600: "#6938EF", | ||
| 700: "#5925DC", | ||
| 800: "#4A1FB8", | ||
| 900: "#3E1C96" | ||
| }, | ||
| teal: { | ||
| 25: "#F6FEFC", | ||
| 50: "#F0FDF9", | ||
| 100: "#CCFBEF", | ||
| 200: "#99F6E0", | ||
| 300: "#5FE9D0", | ||
| 400: "#2ED3B7", | ||
| 500: "#15B79E", | ||
| 600: "#0E9384", | ||
| 700: "#107569", | ||
| 800: "#125D56", | ||
| 900: "#134E48" | ||
| }, | ||
| pink: { | ||
| 25: "#fdf2f8", | ||
| 50: "#fce7f3", | ||
| 100: "#fbcfe8", | ||
| 200: "#f9a8d4", | ||
| 300: "#f472b6", | ||
| 400: "#ec4899", | ||
| 500: "#db2777", | ||
| 600: "#be185d", | ||
| 700: "#9d174d", | ||
| 800: "#831843", | ||
| 900: "#500724" | ||
| }, | ||
| cyan: { | ||
| 25: "#ecfeff", | ||
| 50: "#cffafe", | ||
| 100: "#a5f3fc", | ||
| 200: "#67e8f9", | ||
| 300: "#22d3ee", | ||
| 400: "#06b6d4", | ||
| 500: "#0891b2", | ||
| 600: "#0e7490", | ||
| 700: "#155e75", | ||
| 800: "#164e63", | ||
| 900: "#083344" | ||
| } | ||
| }, | ||
| alpha: { | ||
| 100: "ff", | ||
| 90: "e5", | ||
| 80: "cc", | ||
| 70: "b3", | ||
| 60: "99", | ||
| 50: "80", | ||
| 40: "66", | ||
| 30: "4d", | ||
| 20: "33", | ||
| 10: "1a", | ||
| 0: "00" | ||
| }, | ||
| font: { | ||
| size: { | ||
| "2xs": "calc(var(--tsrd-font-size) * 0.625)", | ||
| xs: "calc(var(--tsrd-font-size) * 0.75)", | ||
| sm: "calc(var(--tsrd-font-size) * 0.875)", | ||
| md: "var(--tsrd-font-size)", | ||
| lg: "calc(var(--tsrd-font-size) * 1.125)", | ||
| xl: "calc(var(--tsrd-font-size) * 1.25)", | ||
| "2xl": "calc(var(--tsrd-font-size) * 1.5)", | ||
| "3xl": "calc(var(--tsrd-font-size) * 1.875)", | ||
| "4xl": "calc(var(--tsrd-font-size) * 2.25)", | ||
| "5xl": "calc(var(--tsrd-font-size) * 3)", | ||
| "6xl": "calc(var(--tsrd-font-size) * 3.75)", | ||
| "7xl": "calc(var(--tsrd-font-size) * 4.5)", | ||
| "8xl": "calc(var(--tsrd-font-size) * 6)", | ||
| "9xl": "calc(var(--tsrd-font-size) * 8)" | ||
| }, | ||
| lineHeight: { | ||
| "3xs": "calc(var(--tsrd-font-size) * 0.75)", | ||
| "2xs": "calc(var(--tsrd-font-size) * 0.875)", | ||
| xs: "calc(var(--tsrd-font-size) * 1)", | ||
| sm: "calc(var(--tsrd-font-size) * 1.25)", | ||
| md: "calc(var(--tsrd-font-size) * 1.5)", | ||
| lg: "calc(var(--tsrd-font-size) * 1.75)", | ||
| xl: "calc(var(--tsrd-font-size) * 2)", | ||
| "2xl": "calc(var(--tsrd-font-size) * 2.25)", | ||
| "3xl": "calc(var(--tsrd-font-size) * 2.5)", | ||
| "4xl": "calc(var(--tsrd-font-size) * 2.75)", | ||
| "5xl": "calc(var(--tsrd-font-size) * 3)", | ||
| "6xl": "calc(var(--tsrd-font-size) * 3.25)", | ||
| "7xl": "calc(var(--tsrd-font-size) * 3.5)", | ||
| "8xl": "calc(var(--tsrd-font-size) * 3.75)", | ||
| "9xl": "calc(var(--tsrd-font-size) * 4)" | ||
| }, | ||
| weight: { | ||
| thin: "100", | ||
| extralight: "200", | ||
| light: "300", | ||
| normal: "400", | ||
| medium: "500", | ||
| semibold: "600", | ||
| bold: "700", | ||
| extrabold: "800", | ||
| black: "900" | ||
| }, | ||
| fontFamily: { | ||
| sans: "ui-sans-serif, Inter, system-ui, sans-serif, sans-serif", | ||
| mono: `ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace` | ||
| } | ||
| }, | ||
| breakpoints: { | ||
| xs: "320px", | ||
| sm: "640px", | ||
| md: "768px", | ||
| lg: "1024px", | ||
| xl: "1280px", | ||
| "2xl": "1536px" | ||
| }, | ||
| border: { radius: { | ||
| none: "0px", | ||
| xs: "calc(var(--tsrd-font-size) * 0.125)", | ||
| sm: "calc(var(--tsrd-font-size) * 0.25)", | ||
| md: "calc(var(--tsrd-font-size) * 0.375)", | ||
| lg: "calc(var(--tsrd-font-size) * 0.5)", | ||
| xl: "calc(var(--tsrd-font-size) * 0.75)", | ||
| "2xl": "calc(var(--tsrd-font-size) * 1)", | ||
| "3xl": "calc(var(--tsrd-font-size) * 1.5)", | ||
| full: "9999px" | ||
| } }, | ||
| size: { | ||
| 0: "0px", | ||
| .25: "calc(var(--tsrd-font-size) * 0.0625)", | ||
| .5: "calc(var(--tsrd-font-size) * 0.125)", | ||
| 1: "calc(var(--tsrd-font-size) * 0.25)", | ||
| 1.5: "calc(var(--tsrd-font-size) * 0.375)", | ||
| 2: "calc(var(--tsrd-font-size) * 0.5)", | ||
| 2.5: "calc(var(--tsrd-font-size) * 0.625)", | ||
| 3: "calc(var(--tsrd-font-size) * 0.75)", | ||
| 3.5: "calc(var(--tsrd-font-size) * 0.875)", | ||
| 4: "calc(var(--tsrd-font-size) * 1)", | ||
| 4.5: "calc(var(--tsrd-font-size) * 1.125)", | ||
| 5: "calc(var(--tsrd-font-size) * 1.25)", | ||
| 5.5: "calc(var(--tsrd-font-size) * 1.375)", | ||
| 6: "calc(var(--tsrd-font-size) * 1.5)", | ||
| 6.5: "calc(var(--tsrd-font-size) * 1.625)", | ||
| 7: "calc(var(--tsrd-font-size) * 1.75)", | ||
| 8: "calc(var(--tsrd-font-size) * 2)", | ||
| 9: "calc(var(--tsrd-font-size) * 2.25)", | ||
| 10: "calc(var(--tsrd-font-size) * 2.5)", | ||
| 11: "calc(var(--tsrd-font-size) * 2.75)", | ||
| 12: "calc(var(--tsrd-font-size) * 3)", | ||
| 14: "calc(var(--tsrd-font-size) * 3.5)", | ||
| 16: "calc(var(--tsrd-font-size) * 4)", | ||
| 20: "calc(var(--tsrd-font-size) * 5)", | ||
| 24: "calc(var(--tsrd-font-size) * 6)", | ||
| 28: "calc(var(--tsrd-font-size) * 7)", | ||
| 32: "calc(var(--tsrd-font-size) * 8)", | ||
| 36: "calc(var(--tsrd-font-size) * 9)", | ||
| 40: "calc(var(--tsrd-font-size) * 10)", | ||
| 44: "calc(var(--tsrd-font-size) * 11)", | ||
| 48: "calc(var(--tsrd-font-size) * 12)", | ||
| 52: "calc(var(--tsrd-font-size) * 13)", | ||
| 56: "calc(var(--tsrd-font-size) * 14)", | ||
| 60: "calc(var(--tsrd-font-size) * 15)", | ||
| 64: "calc(var(--tsrd-font-size) * 16)", | ||
| 72: "calc(var(--tsrd-font-size) * 18)", | ||
| 80: "calc(var(--tsrd-font-size) * 20)", | ||
| 96: "calc(var(--tsrd-font-size) * 24)" | ||
| }, | ||
| shadow: { | ||
| xs: (_ = "rgb(0 0 0 / 0.1)") => `0 1px 2px 0 rgb(0 0 0 / 0.05)`, | ||
| sm: (color = "rgb(0 0 0 / 0.1)") => `0 1px 3px 0 ${color}, 0 1px 2px -1px ${color}`, | ||
| md: (color = "rgb(0 0 0 / 0.1)") => `0 4px 6px -1px ${color}, 0 2px 4px -2px ${color}`, | ||
| lg: (color = "rgb(0 0 0 / 0.1)") => `0 10px 15px -3px ${color}, 0 4px 6px -4px ${color}`, | ||
| xl: (color = "rgb(0 0 0 / 0.1)") => `0 20px 25px -5px ${color}, 0 8px 10px -6px ${color}`, | ||
| "2xl": (color = "rgb(0 0 0 / 0.25)") => `0 25px 50px -12px ${color}`, | ||
| inner: (color = "rgb(0 0 0 / 0.05)") => `inset 0 2px 4px 0 ${color}`, | ||
| none: () => `none` | ||
| }, | ||
| zIndices: { | ||
| hide: -1, | ||
| auto: "auto", | ||
| base: 0, | ||
| docked: 10, | ||
| dropdown: 1e3, | ||
| sticky: 1100, | ||
| banner: 1200, | ||
| overlay: 1300, | ||
| modal: 1400, | ||
| popover: 1500, | ||
| skipLink: 1600, | ||
| toast: 1700, | ||
| tooltip: 1800 | ||
| } | ||
| }; | ||
| //#endregion | ||
| //#region src/styles/use-styles.ts | ||
| const stylesFactory = (theme) => { | ||
| const { colors, font, size, alpha, border } = tokens; | ||
| const { fontFamily, size: fontSize } = font; | ||
| const css = goober.css; | ||
| const t = (light, dark) => theme === "light" ? light : dark; | ||
| return { | ||
| mainContainer: css` | ||
| display: flex; | ||
| flex: 1; | ||
| min-height: 0; | ||
| overflow: hidden; | ||
| padding: ${size[2]}; | ||
| padding-top: 0; | ||
| margin-top: ${size[2]}; | ||
| `, | ||
| dragHandle: css` | ||
| width: 8px; | ||
| background: ${t(colors.gray[300], colors.darkGray[600])}; | ||
| cursor: col-resize; | ||
| position: relative; | ||
| transition: all 0.2s ease; | ||
| user-select: none; | ||
| pointer-events: all; | ||
| margin: 0 ${size[1]}; | ||
| border-radius: 2px; | ||
| &:hover { | ||
| background: ${t(colors.blue[600], colors.blue[500])}; | ||
| margin: 0 ${size[1]}; | ||
| } | ||
| &.dragging { | ||
| background: ${t(colors.blue[700], colors.blue[600])}; | ||
| margin: 0 ${size[1]}; | ||
| } | ||
| &::after { | ||
| content: ''; | ||
| position: absolute; | ||
| top: 50%; | ||
| left: 50%; | ||
| transform: translate(-50%, -50%); | ||
| width: 2px; | ||
| height: 20px; | ||
| background: ${t(colors.gray[400], colors.darkGray[400])}; | ||
| border-radius: 1px; | ||
| pointer-events: none; | ||
| } | ||
| &:hover::after, | ||
| &.dragging::after { | ||
| background: ${t(colors.blue[500], colors.blue[300])}; | ||
| } | ||
| `, | ||
| leftPanel: css` | ||
| background: ${t(colors.gray[100], colors.darkGray[800])}; | ||
| border-radius: ${border.radius.lg}; | ||
| border: 1px solid ${t(colors.gray[200], colors.darkGray[700])}; | ||
| display: flex; | ||
| flex-direction: column; | ||
| overflow: hidden; | ||
| min-height: 0; | ||
| flex-shrink: 0; | ||
| `, | ||
| rightPanel: css` | ||
| background: ${t(colors.gray[100], colors.darkGray[800])}; | ||
| border-radius: ${border.radius.lg}; | ||
| border: 1px solid ${t(colors.gray[200], colors.darkGray[700])}; | ||
| display: flex; | ||
| flex-direction: column; | ||
| overflow: hidden; | ||
| min-height: 0; | ||
| flex: 1; | ||
| `, | ||
| panelHeader: css` | ||
| font-size: ${fontSize.md}; | ||
| font-weight: ${font.weight.bold}; | ||
| color: ${t(colors.blue[700], colors.blue[400])}; | ||
| padding: ${size[2]}; | ||
| border-bottom: 1px solid ${t(colors.gray[200], colors.darkGray[700])}; | ||
| background: ${t(colors.gray[100], colors.darkGray[800])}; | ||
| flex-shrink: 0; | ||
| `, | ||
| utilList: css` | ||
| flex: 1; | ||
| overflow-y: auto; | ||
| padding: ${size[1]}; | ||
| min-height: 0; | ||
| `, | ||
| utilGroup: css` | ||
| margin-bottom: ${size[2]}; | ||
| `, | ||
| utilGroupHeader: css` | ||
| font-size: ${fontSize.xs}; | ||
| font-weight: ${font.weight.semibold}; | ||
| color: ${t(colors.gray[600], colors.gray[400])}; | ||
| text-transform: uppercase; | ||
| letter-spacing: 0.05em; | ||
| margin-bottom: ${size[1]}; | ||
| padding: ${size[1]} ${size[2]}; | ||
| background: ${t(colors.gray[200], colors.darkGray[700])}; | ||
| border-radius: ${border.radius.md}; | ||
| `, | ||
| utilRow: css` | ||
| display: flex; | ||
| justify-content: space-between; | ||
| align-items: center; | ||
| padding: ${size[2]}; | ||
| margin-bottom: ${size[1]}; | ||
| background: ${t(colors.gray[200], colors.darkGray[700])}; | ||
| border-radius: ${border.radius.md}; | ||
| cursor: pointer; | ||
| transition: all 0.2s ease; | ||
| border: 1px solid transparent; | ||
| &:hover { | ||
| background: ${t(colors.gray[300], colors.darkGray[600])}; | ||
| border-color: ${t(colors.gray[400], colors.darkGray[500])}; | ||
| } | ||
| `, | ||
| utilRowSelected: css` | ||
| background: ${t(colors.blue[100], colors.blue[900] + alpha[20])}; | ||
| border-color: ${t(colors.blue[600], colors.blue[500])}; | ||
| box-shadow: 0 0 0 1px | ||
| ${t(colors.blue[600] + alpha[30], colors.blue[500] + alpha[30])}; | ||
| `, | ||
| utilKey: css` | ||
| font-family: ${fontFamily.mono}; | ||
| font-size: ${fontSize.xs}; | ||
| color: ${t(colors.gray[900], colors.gray[100])}; | ||
| flex: 1; | ||
| overflow: hidden; | ||
| text-overflow: ellipsis; | ||
| white-space: nowrap; | ||
| `, | ||
| utilStatus: css` | ||
| font-size: ${fontSize.xs}; | ||
| color: ${t(colors.gray[600], colors.gray[400])}; | ||
| text-transform: uppercase; | ||
| letter-spacing: 0.05em; | ||
| padding: ${size[1]} ${size[1]}; | ||
| background: ${t(colors.gray[300], colors.darkGray[600])}; | ||
| border-radius: ${border.radius.sm}; | ||
| margin-left: ${size[1]}; | ||
| `, | ||
| stateDetails: css` | ||
| flex: 1; | ||
| overflow-y: auto; | ||
| padding: ${size[2]}; | ||
| min-height: 0; | ||
| `, | ||
| stateHeader: css` | ||
| margin-bottom: ${size[2]}; | ||
| padding-bottom: ${size[2]}; | ||
| border-bottom: 1px solid ${t(colors.gray[200], colors.darkGray[700])}; | ||
| `, | ||
| stateTitle: css` | ||
| font-size: ${fontSize.md}; | ||
| font-weight: ${font.weight.bold}; | ||
| color: ${t(colors.blue[700], colors.blue[400])}; | ||
| margin-bottom: ${size[1]}; | ||
| `, | ||
| stateKey: css` | ||
| font-family: ${fontFamily.mono}; | ||
| font-size: ${fontSize.xs}; | ||
| color: ${t(colors.gray[600], colors.gray[400])}; | ||
| word-break: break-all; | ||
| `, | ||
| stateContent: css` | ||
| background: ${t(colors.gray[100], colors.darkGray[700])}; | ||
| border-radius: ${border.radius.md}; | ||
| padding: ${size[2]}; | ||
| border: 1px solid ${t(colors.gray[300], colors.darkGray[600])}; | ||
| `, | ||
| detailsGrid: css` | ||
| display: grid; | ||
| grid-template-columns: 1fr; | ||
| gap: ${size[2]}; | ||
| align-items: start; | ||
| `, | ||
| detailSection: css` | ||
| background: ${t(colors.white, colors.darkGray[700])}; | ||
| border: 1px solid ${t(colors.gray[300], colors.darkGray[600])}; | ||
| border-radius: ${border.radius.md}; | ||
| padding: ${size[2]}; | ||
| `, | ||
| detailSectionHeader: css` | ||
| font-size: ${fontSize.sm}; | ||
| font-weight: ${font.weight.bold}; | ||
| color: ${t(colors.gray[800], colors.gray[200])}; | ||
| margin-bottom: ${size[1]}; | ||
| text-transform: uppercase; | ||
| letter-spacing: 0.04em; | ||
| `, | ||
| actionsRow: css` | ||
| display: flex; | ||
| flex-wrap: wrap; | ||
| gap: ${size[2]}; | ||
| `, | ||
| actionButton: css` | ||
| display: inline-flex; | ||
| align-items: center; | ||
| gap: ${size[1]}; | ||
| padding: ${size[1]} ${size[2]}; | ||
| border-radius: ${border.radius.md}; | ||
| border: 1px solid ${t(colors.gray[300], colors.darkGray[500])}; | ||
| background: ${t(colors.gray[200], colors.darkGray[600])}; | ||
| color: ${t(colors.gray[900], colors.gray[100])}; | ||
| font-size: ${fontSize.xs}; | ||
| cursor: pointer; | ||
| user-select: none; | ||
| transition: | ||
| background 0.15s, | ||
| border-color 0.15s; | ||
| &:hover { | ||
| background: ${t(colors.gray[300], colors.darkGray[500])}; | ||
| border-color: ${t(colors.gray[400], colors.darkGray[400])}; | ||
| } | ||
| &:disabled { | ||
| opacity: 0.5; | ||
| cursor: not-allowed; | ||
| &:hover { | ||
| background: ${t(colors.gray[200], colors.darkGray[600])}; | ||
| border-color: ${t(colors.gray[300], colors.darkGray[500])}; | ||
| } | ||
| } | ||
| `, | ||
| actionDotBlue: css` | ||
| width: 6px; | ||
| height: 6px; | ||
| border-radius: 9999px; | ||
| background: ${colors.blue[400]}; | ||
| `, | ||
| actionDotGreen: css` | ||
| width: 6px; | ||
| height: 6px; | ||
| border-radius: 9999px; | ||
| background: ${colors.green[400]}; | ||
| `, | ||
| actionDotRed: css` | ||
| width: 6px; | ||
| height: 6px; | ||
| border-radius: 9999px; | ||
| background: ${colors.red[400]}; | ||
| `, | ||
| actionDotYellow: css` | ||
| width: 6px; | ||
| height: 6px; | ||
| border-radius: 9999px; | ||
| background: ${colors.yellow[400]}; | ||
| `, | ||
| actionDotOrange: css` | ||
| width: 6px; | ||
| height: 6px; | ||
| border-radius: 9999px; | ||
| background: ${colors.pink[400]}; | ||
| `, | ||
| actionDotPurple: css` | ||
| width: 6px; | ||
| height: 6px; | ||
| border-radius: 9999px; | ||
| background: ${colors.purple[400]}; | ||
| `, | ||
| infoGrid: css` | ||
| display: grid; | ||
| grid-template-columns: auto 1fr; | ||
| gap: ${size[1]}; | ||
| row-gap: ${size[1]}; | ||
| align-items: center; | ||
| `, | ||
| infoLabel: css` | ||
| color: ${t(colors.gray[600], colors.gray[400])}; | ||
| font-size: ${fontSize.xs}; | ||
| text-transform: uppercase; | ||
| letter-spacing: 0.05em; | ||
| `, | ||
| infoValueMono: css` | ||
| font-family: ${fontFamily.mono}; | ||
| font-size: ${fontSize.xs}; | ||
| color: ${t(colors.gray[900], colors.gray[100])}; | ||
| word-break: break-all; | ||
| `, | ||
| noSelection: css` | ||
| flex: 1; | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: center; | ||
| color: ${t(colors.gray[500], colors.gray[500])}; | ||
| font-style: italic; | ||
| text-align: center; | ||
| padding: ${size[4]}; | ||
| `, | ||
| sectionContainer: css` | ||
| display: flex; | ||
| flex-wrap: wrap; | ||
| gap: ${size[4]}; | ||
| `, | ||
| section: css` | ||
| background: ${t(colors.gray[100], colors.darkGray[800])}; | ||
| border-radius: ${border.radius.lg}; | ||
| box-shadow: ${tokens.shadow.md(t(colors.gray[400] + alpha[80], colors.black + alpha[80]))}; | ||
| padding: ${size[4]}; | ||
| margin-bottom: ${size[4]}; | ||
| border: 1px solid ${t(colors.gray[200], colors.darkGray[700])}; | ||
| min-width: 0; | ||
| max-width: 33%; | ||
| max-height: fit-content; | ||
| `, | ||
| sectionHeader: css` | ||
| font-size: ${fontSize.lg}; | ||
| font-weight: ${font.weight.bold}; | ||
| margin-bottom: ${size[2]}; | ||
| color: ${t(colors.blue[600], colors.blue[400])}; | ||
| letter-spacing: 0.01em; | ||
| display: flex; | ||
| align-items: center; | ||
| gap: ${size[2]}; | ||
| `, | ||
| sectionEmpty: css` | ||
| color: ${t(colors.gray[500], colors.gray[500])}; | ||
| font-size: ${fontSize.sm}; | ||
| font-style: italic; | ||
| margin: ${size[2]} 0; | ||
| `, | ||
| instanceList: css` | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: ${size[2]}; | ||
| background: ${t(colors.gray[200], colors.darkGray[700])}; | ||
| border: 1px solid ${t(colors.gray[300], colors.darkGray[600])}; | ||
| `, | ||
| instanceCard: css` | ||
| background: ${t(colors.gray[200], colors.darkGray[700])}; | ||
| border-radius: ${border.radius.md}; | ||
| padding: ${size[3]}; | ||
| border: 1px solid ${t(colors.gray[300], colors.darkGray[600])}; | ||
| font-size: ${fontSize.sm}; | ||
| color: ${t(colors.gray[900], colors.gray[100])}; | ||
| font-family: ${fontFamily.mono}; | ||
| overflow-x: auto; | ||
| transition: | ||
| box-shadow 0.3s, | ||
| background 0.3s; | ||
| ` | ||
| }; | ||
| }; | ||
| function useStyles() { | ||
| const { theme } = useTheme(); | ||
| const [styles, setStyles] = createSignal(stylesFactory(theme())); | ||
| createEffect(() => { | ||
| setStyles(stylesFactory(theme())); | ||
| }); | ||
| return styles; | ||
| } | ||
| //#endregion | ||
| //#region src/components/util-groups.ts | ||
| const UTIL_GROUPS = [ | ||
| { | ||
| key: "debouncers", | ||
| label: "Debouncers", | ||
| displayName: "Debouncer" | ||
| }, | ||
| { | ||
| key: "throttlers", | ||
| label: "Throttlers", | ||
| displayName: "Throttler" | ||
| }, | ||
| { | ||
| key: "batchers", | ||
| label: "Batchers", | ||
| displayName: "Batcher" | ||
| }, | ||
| { | ||
| key: "queuers", | ||
| label: "Queuers", | ||
| displayName: "Queuer" | ||
| }, | ||
| { | ||
| key: "rateLimiters", | ||
| label: "Rate Limiters", | ||
| displayName: "Rate Limiter" | ||
| }, | ||
| { | ||
| key: "asyncDebouncers", | ||
| label: "Async Debouncers", | ||
| displayName: "Async Debouncer" | ||
| }, | ||
| { | ||
| key: "asyncThrottlers", | ||
| label: "Async Throttlers", | ||
| displayName: "Async Throttler" | ||
| }, | ||
| { | ||
| key: "asyncBatchers", | ||
| label: "Async Batchers", | ||
| displayName: "Async Batcher" | ||
| }, | ||
| { | ||
| key: "asyncQueuers", | ||
| label: "Async Queuers", | ||
| displayName: "Async Queuer" | ||
| }, | ||
| { | ||
| key: "asyncRateLimiters", | ||
| label: "Async Rate Limiters", | ||
| displayName: "Async Rate Limiter" | ||
| } | ||
| ]; | ||
| //#endregion | ||
| //#region src/components/UtilList.tsx | ||
| var _tmpl$$4 = [ | ||
| "<div", | ||
| ">", | ||
| "</div>" | ||
| ], _tmpl$2$2 = [ | ||
| "<div", | ||
| "><div", | ||
| ">", | ||
| "</div>", | ||
| "</div>" | ||
| ], _tmpl$3$2 = [ | ||
| "<div", | ||
| "><div", | ||
| ">", | ||
| "</div><div", | ||
| ">", | ||
| "</div></div>" | ||
| ]; | ||
| function UtilList(props) { | ||
| const styles = useStyles(); | ||
| return ssr(_tmpl$$4, ssrAttribute("class", escape(styles().utilList, true), false), escape(createComponent(For, { | ||
| each: UTIL_GROUPS, | ||
| children: (group) => props.utilState()[group.key].length > 0 && ssr(_tmpl$2$2, ssrAttribute("class", escape(styles().utilGroup, true), false), ssrAttribute("class", escape(styles().utilGroupHeader, true), false), escape(group.label), escape(createComponent(For, { | ||
| get each() { | ||
| return props.utilState()[group.key]; | ||
| }, | ||
| children: (instance) => { | ||
| const status = (() => { | ||
| try { | ||
| const statusAccessor = useStore(instance.store, (s) => s.status); | ||
| return () => statusAccessor() ?? "unknown"; | ||
| } catch { | ||
| return () => "unknown"; | ||
| } | ||
| })(); | ||
| return ssr(_tmpl$3$2, ssrAttribute("class", escape(clsx(styles().utilRow, props.selectedKey() === instance.key && styles().utilRowSelected), true), false), ssrAttribute("class", escape(styles().utilKey, true), false), escape(instance.key), ssrAttribute("class", escape(styles().utilStatus, true), false), escape(status())); | ||
| } | ||
| }))) | ||
| }))); | ||
| } | ||
| //#endregion | ||
| //#region src/components/ActionButtons.tsx | ||
| var _tmpl$$3 = ["<div", ">No actions available for this util</div>"], _tmpl$2$1 = [ | ||
| "<div", | ||
| ">", | ||
| "", | ||
| "", | ||
| "", | ||
| "", | ||
| "", | ||
| "</div>" | ||
| ], _tmpl$3$1 = [ | ||
| "<button", | ||
| "><span", | ||
| "></span>", | ||
| "</button>" | ||
| ], _tmpl$4 = [ | ||
| "<button", | ||
| "", | ||
| "><span", | ||
| "></span>Flush</button>" | ||
| ], _tmpl$5 = [ | ||
| "<button", | ||
| "", | ||
| "><span", | ||
| "></span>Cancel</button>" | ||
| ], _tmpl$6 = [ | ||
| "<button", | ||
| "><span", | ||
| "></span>Reset</button>" | ||
| ], _tmpl$7 = [ | ||
| "<button", | ||
| "", | ||
| "><span", | ||
| "></span>Clear</button>" | ||
| ]; | ||
| function ActionButtons(props) { | ||
| const styles = useStyles(); | ||
| const utilInstance = props.instance; | ||
| const utilState = utilInstance?.store?.state; | ||
| const hasPending = utilState && "isPending" in utilState; | ||
| const hasEmpty = utilState && "isEmpty" in utilState; | ||
| const isPending = hasPending ? !!utilState.isPending : false; | ||
| const isEmpty = hasEmpty ? utilState.isEmpty : void 0; | ||
| const hasFlush = typeof utilInstance.flush === "function"; | ||
| const hasCancel = typeof utilInstance.cancel === "function"; | ||
| const hasReset = typeof utilInstance.reset === "function"; | ||
| const hasClear = typeof utilInstance.clear === "function"; | ||
| const hasStart = typeof utilInstance.start === "function"; | ||
| const hasStop = typeof utilInstance.stop === "function"; | ||
| const hasStartStop = hasStart && hasStop; | ||
| const isRunning = utilState?.isRunning ?? true; | ||
| if (!hasPending && !hasFlush && !hasCancel && !hasReset && !hasClear) return ssr(_tmpl$$3, ssrAttribute("class", escape(styles().sectionEmpty, true), false)); | ||
| `${props.utilName}`; | ||
| return ssr(_tmpl$2$1, ssrAttribute("class", escape(styles().actionsRow, true), false), hasPending && ssr(_tmpl$3$1, ssrAttribute("class", escape(styles().actionButton, true), false), ssrAttribute("class", escape(styles().actionDotBlue, true), false), isPending ? "Restore Pending" : "Trigger Pending"), hasFlush && ssr(_tmpl$4, ssrAttribute("class", escape(styles().actionButton, true), false), ssrAttribute("disabled", props.utilName.toLowerCase().includes("debouncer") && !isPending || props.utilName.toLowerCase().includes("throttler") && !isPending || props.utilName.toLowerCase().includes("batcher") && isEmpty || props.utilName.toLowerCase().includes("queuer") && isEmpty, true), ssrAttribute("class", escape(styles().actionDotGreen, true), false)), hasCancel && ssr(_tmpl$5, ssrAttribute("class", escape(styles().actionButton, true), false), ssrAttribute("disabled", props.utilName.toLowerCase().includes("debouncer") && !isPending || props.utilName.toLowerCase().includes("throttler") && !isPending || props.utilName.toLowerCase().includes("batcher") && isEmpty || props.utilName.toLowerCase().includes("queuer") && isEmpty, true), ssrAttribute("class", escape(styles().actionDotRed, true), false)), hasReset && ssr(_tmpl$6, ssrAttribute("class", escape(styles().actionButton, true), false), ssrAttribute("class", escape(styles().actionDotYellow, true), false)), hasClear && ssr(_tmpl$7, ssrAttribute("class", escape(styles().actionButton, true), false), ssrAttribute("disabled", props.utilName.toLowerCase().includes("batcher") && isEmpty || props.utilName.toLowerCase().includes("queuer") && isEmpty, true), ssrAttribute("class", escape(styles().actionDotOrange, true), false)), hasStartStop && ssr(_tmpl$3$1, ssrAttribute("class", escape(styles().actionButton, true), false), ssrAttribute("class", escape(styles().actionDotPurple, true), false), isRunning ? "Stop" : "Start")); | ||
| } | ||
| //#endregion | ||
| //#region src/components/StateHeader.tsx | ||
| var _tmpl$$2 = [ | ||
| "<div", | ||
| "><div", | ||
| ">", | ||
| "</div><div style=\"", | ||
| "\"><div", | ||
| "><div", | ||
| ">Key</div><div", | ||
| ">", | ||
| "</div><div", | ||
| ">Last Updated</div><div", | ||
| ">", | ||
| " (", | ||
| ")</div></div><div", | ||
| " style=\"", | ||
| "\">", | ||
| "% reduction</div></div></div>" | ||
| ]; | ||
| dayjs.extend(relativeTime); | ||
| function StateHeader(props) { | ||
| const styles = useStyles(); | ||
| const [now, setNow] = createSignal(Date.now()); | ||
| onMount(() => { | ||
| const interval = setInterval(() => { | ||
| setNow(Date.now()); | ||
| }, 1e3); | ||
| onCleanup(() => { | ||
| clearInterval(interval); | ||
| }); | ||
| }); | ||
| const entry = props.selectedInstance(); | ||
| if (!entry) return null; | ||
| const key = entry.instance.key; | ||
| const updatedAt = props.utilState().lastUpdatedByKey[key] ?? Date.now(); | ||
| const getRelativeTime = () => { | ||
| const diffMs = now() - updatedAt; | ||
| const diffSeconds = Math.floor(diffMs / 1e3); | ||
| if (diffSeconds < 60) return `${diffSeconds} second${diffSeconds !== 1 ? "s" : ""} ago`; | ||
| return dayjs(updatedAt).fromNow(); | ||
| }; | ||
| const getReductionPercentage = () => { | ||
| const state = entry.instance.store?.state || entry.instance.state; | ||
| if (!state) return 0; | ||
| const completedExecutions = entry.type.toLowerCase().includes("async") ? state.settleCount || 0 : state.executionCount || 0; | ||
| if (entry.type.toLowerCase().includes("batcher")) { | ||
| const totalItemsProcessed = state.totalItemsProcessed || 0; | ||
| if (totalItemsProcessed === 0) return 0; | ||
| return Math.round((totalItemsProcessed - completedExecutions) / totalItemsProcessed * 100); | ||
| } | ||
| let requestCount = 0; | ||
| if (state.maybeExecuteCount !== void 0) requestCount = state.maybeExecuteCount; | ||
| else if (state.addItemCount !== void 0) requestCount = state.addItemCount; | ||
| else return 0; | ||
| if (requestCount === 0) return 0; | ||
| const reduction = requestCount - completedExecutions; | ||
| return Math.round(reduction / requestCount * 100); | ||
| }; | ||
| const reductionPercentage = getReductionPercentage(); | ||
| return ssr(_tmpl$$2, ssrAttribute("class", escape(styles().stateHeader, true), false), ssrAttribute("class", escape(styles().stateTitle, true), false), escape(entry.type), ssrStyleProperty("display:", "flex") + ssrStyleProperty(";align-items:", "center") + ssrStyleProperty(";gap:", "16px"), ssrAttribute("class", escape(styles().infoGrid, true), false), ssrAttribute("class", escape(styles().infoLabel, true), false), ssrAttribute("class", escape(styles().infoValueMono, true), false), escape(key), ssrAttribute("class", escape(styles().infoLabel, true), false), ssrAttribute("class", escape(styles().infoValueMono, true), false), escape(new Date(updatedAt).toLocaleTimeString()), escape(getRelativeTime()), ssrAttribute("class", escape(styles().infoValueMono, true), false), ssrStyleProperty("margin-left:", "auto") + ssrStyleProperty(";font-weight:", "bold"), escape(reductionPercentage)); | ||
| } | ||
| //#endregion | ||
| //#region src/components/DetailsPanel.tsx | ||
| var _tmpl$$1 = [ | ||
| "<div", | ||
| ">", | ||
| "</div>" | ||
| ], _tmpl$2 = ["<div", ">Select a util from the left panel to view its state</div>"], _tmpl$3 = [ | ||
| "<div", | ||
| "><div", | ||
| "><div", | ||
| ">Actions</div>", | ||
| "</div><div", | ||
| "><div", | ||
| ">State</div><div", | ||
| ">", | ||
| "</div></div><div", | ||
| "><div", | ||
| ">Options</div><div", | ||
| ">", | ||
| "</div></div></div>" | ||
| ]; | ||
| function DetailsPanel(props) { | ||
| const styles = useStyles(); | ||
| return ssr(_tmpl$$1, ssrAttribute("class", escape(styles().stateDetails, true), false), (() => { | ||
| const entry = props.selectedInstance(); | ||
| if (!entry) return ssr(_tmpl$2, ssrAttribute("class", escape(styles().noSelection, true), false)); | ||
| return escape([createComponent(StateHeader, { | ||
| get selectedInstance() { | ||
| return props.selectedInstance; | ||
| }, | ||
| get utilState() { | ||
| return props.utilState; | ||
| } | ||
| }), ssr(_tmpl$3, ssrAttribute("class", escape(styles().detailsGrid, true), false), ssrAttribute("class", escape(styles().detailSection, true), false), ssrAttribute("class", escape(styles().detailSectionHeader, true), false), escape(createComponent(ActionButtons, { | ||
| get instance() { | ||
| return entry.instance; | ||
| }, | ||
| get utilName() { | ||
| return entry.type; | ||
| } | ||
| })), ssrAttribute("class", escape(styles().detailSection, true), false), ssrAttribute("class", escape(styles().detailSectionHeader, true), false), ssrAttribute("class", escape(styles().stateContent, true), false), escape(createComponent(JsonTree, { get value() { | ||
| return entry.instance.store?.state; | ||
| } })), ssrAttribute("class", escape(styles().detailSection, true), false), ssrAttribute("class", escape(styles().detailSectionHeader, true), false), ssrAttribute("class", escape(styles().stateContent, true), false), escape(createComponent(JsonTree, { get value() { | ||
| return entry.instance.options; | ||
| } })))]); | ||
| })()); | ||
| } | ||
| //#endregion | ||
| //#region src/components/Shell.tsx | ||
| var _tmpl$ = [ | ||
| "<div", | ||
| "><div", | ||
| " style=\"", | ||
| "\">", | ||
| "</div><div class=\"", | ||
| "\"></div><div", | ||
| " style=\"", | ||
| "\"><div", | ||
| ">Details</div>", | ||
| "</div></div>" | ||
| ]; | ||
| function Shell() { | ||
| const styles = useStyles(); | ||
| const state = usePacerDevtoolsState(); | ||
| const utilState = () => state; | ||
| const [selectedKey, setSelectedKey] = createSignal(null); | ||
| const [leftPanelWidth, setLeftPanelWidth] = createSignal(300); | ||
| const [isDragging, setIsDragging] = createSignal(false); | ||
| const selectedInstance = createMemo(() => { | ||
| const key = selectedKey(); | ||
| if (!key) return null; | ||
| for (const group of UTIL_GROUPS) { | ||
| const instance = utilState()[group.key].find((inst) => inst.key === key); | ||
| if (instance) return { | ||
| instance, | ||
| type: group.displayName | ||
| }; | ||
| } | ||
| return null; | ||
| }); | ||
| let dragStartX = 0; | ||
| let dragStartWidth = 0; | ||
| const handleMouseMove = (e) => { | ||
| if (!isDragging()) return; | ||
| e.preventDefault(); | ||
| const deltaX = e.clientX - dragStartX; | ||
| setLeftPanelWidth(Math.max(150, Math.min(800, dragStartWidth + deltaX))); | ||
| }; | ||
| const handleMouseUp = () => { | ||
| setIsDragging(false); | ||
| document.body.style.cursor = ""; | ||
| document.body.style.userSelect = ""; | ||
| }; | ||
| onMount(() => { | ||
| document.addEventListener("mousemove", handleMouseMove); | ||
| document.addEventListener("mouseup", handleMouseUp); | ||
| }); | ||
| onCleanup(() => { | ||
| document.removeEventListener("mousemove", handleMouseMove); | ||
| document.removeEventListener("mouseup", handleMouseUp); | ||
| }); | ||
| return createComponent(MainPanel, { get children() { | ||
| return [createComponent(Header, { get children() { | ||
| return createComponent(HeaderLogo, { | ||
| flavor: { | ||
| light: "#84cc16", | ||
| dark: "#84cc16" | ||
| }, | ||
| children: "TanStack Pacer" | ||
| }); | ||
| } }), ssr(_tmpl$, ssrAttribute("class", escape(styles().mainContainer, true), false), ssrAttribute("class", escape(styles().leftPanel, true), false), ssrStyleProperty("width:", `${escape(leftPanelWidth(), true)}px`) + ssrStyleProperty(";min-width:", "150px") + ssrStyleProperty(";max-width:", "800px"), escape(createComponent(UtilList, { | ||
| selectedKey, | ||
| setSelectedKey, | ||
| utilState | ||
| })), `${escape(styles().dragHandle, true)} ${isDragging() ? "dragging" : ""}`, ssrAttribute("class", escape(styles().rightPanel, true), false), ssrStyleProperty("flex:", 1), ssrAttribute("class", escape(styles().panelHeader, true), false), escape(createComponent(DetailsPanel, { | ||
| selectedInstance, | ||
| utilState | ||
| })))]; | ||
| } }); | ||
| } | ||
| //#endregion | ||
| //#region src/components/index.tsx | ||
| function PacerDevtools(props) { | ||
| return createComponent(ThemeContextProvider, { | ||
| get theme() { | ||
| return props.theme; | ||
| }, | ||
| get children() { | ||
| return createComponent(PacerContextProvider, { get children() { | ||
| return createComponent(Shell, {}); | ||
| } }); | ||
| } | ||
| }); | ||
| } | ||
| //#endregion | ||
| export { PacerDevtools as default }; |
| import { className, createComponent, delegateEvents, effect, insert, memo, setStyleProperty, template } from "solid-js/web"; | ||
| import { Header, HeaderLogo, JsonTree, MainPanel, ThemeContextProvider, useTheme } from "@tanstack/devtools-ui"; | ||
| import { createStore } from "solid-js/store"; | ||
| import { For, createContext, createEffect, createMemo, createSignal, onCleanup, onMount, useContext } from "solid-js"; | ||
| import { pacerEventClient } from "@tanstack/pacer/event-client"; | ||
| import * as goober from "goober"; | ||
| import { useStore } from "@tanstack/solid-store"; | ||
| import clsx from "clsx"; | ||
| import { pacerEventClient as pacerEventClient$1 } from "@tanstack/pacer"; | ||
| import dayjs from "dayjs"; | ||
| import relativeTime from "dayjs/plugin/relativeTime.js"; | ||
| //#region src/PacerContextProvider.tsx | ||
| const initialPacerDevtoolsStore = { | ||
| asyncBatchers: [], | ||
| asyncDebouncers: [], | ||
| asyncQueuers: [], | ||
| asyncRateLimiters: [], | ||
| asyncThrottlers: [], | ||
| batchers: [], | ||
| debouncers: [], | ||
| queuers: [], | ||
| rateLimiters: [], | ||
| throttlers: [], | ||
| lastUpdatedByKey: {} | ||
| }; | ||
| const PacerDevtoolsContext = createContext([initialPacerDevtoolsStore, () => {}]); | ||
| const updateOrAddToArray = (oldArray, newItem) => { | ||
| const index = oldArray.findIndex((item) => item.key === newItem.key); | ||
| if (index !== -1) return oldArray.map((item, i) => i === index ? newItem : item); | ||
| return [...oldArray, newItem]; | ||
| }; | ||
| function PacerContextProvider(props) { | ||
| const [store, setStore] = createStore(initialPacerDevtoolsStore); | ||
| createEffect(() => { | ||
| onCleanup(pacerEventClient.onAllPluginEvents((_e) => { | ||
| const e = _e; | ||
| switch (e.type) { | ||
| case "pacer:AsyncBatcher": | ||
| setStore({ | ||
| asyncBatchers: updateOrAddToArray(store.asyncBatchers, e.payload), | ||
| lastUpdatedByKey: { | ||
| ...store.lastUpdatedByKey, | ||
| [e.payload.key]: Date.now() | ||
| } | ||
| }); | ||
| break; | ||
| case "pacer:AsyncDebouncer": | ||
| setStore({ | ||
| asyncDebouncers: updateOrAddToArray(store.asyncDebouncers, e.payload), | ||
| lastUpdatedByKey: { | ||
| ...store.lastUpdatedByKey, | ||
| [e.payload.key]: Date.now() | ||
| } | ||
| }); | ||
| break; | ||
| case "pacer:AsyncQueuer": | ||
| setStore({ | ||
| asyncQueuers: updateOrAddToArray(store.asyncQueuers, e.payload), | ||
| lastUpdatedByKey: { | ||
| ...store.lastUpdatedByKey, | ||
| [e.payload.key]: Date.now() | ||
| } | ||
| }); | ||
| break; | ||
| case "pacer:AsyncRateLimiter": | ||
| setStore({ | ||
| asyncRateLimiters: updateOrAddToArray(store.asyncRateLimiters, e.payload), | ||
| lastUpdatedByKey: { | ||
| ...store.lastUpdatedByKey, | ||
| [e.payload.key]: Date.now() | ||
| } | ||
| }); | ||
| break; | ||
| case "pacer:AsyncThrottler": | ||
| setStore({ | ||
| asyncThrottlers: updateOrAddToArray(store.asyncThrottlers, e.payload), | ||
| lastUpdatedByKey: { | ||
| ...store.lastUpdatedByKey, | ||
| [e.payload.key]: Date.now() | ||
| } | ||
| }); | ||
| break; | ||
| case "pacer:Batcher": | ||
| setStore({ | ||
| batchers: updateOrAddToArray(store.batchers, e.payload), | ||
| lastUpdatedByKey: { | ||
| ...store.lastUpdatedByKey, | ||
| [e.payload.key]: Date.now() | ||
| } | ||
| }); | ||
| break; | ||
| case "pacer:Debouncer": | ||
| setStore({ | ||
| debouncers: updateOrAddToArray(store.debouncers, e.payload), | ||
| lastUpdatedByKey: { | ||
| ...store.lastUpdatedByKey, | ||
| [e.payload.key]: Date.now() | ||
| } | ||
| }); | ||
| break; | ||
| case "pacer:Queuer": | ||
| setStore({ | ||
| queuers: updateOrAddToArray(store.queuers, e.payload), | ||
| lastUpdatedByKey: { | ||
| ...store.lastUpdatedByKey, | ||
| [e.payload.key]: Date.now() | ||
| } | ||
| }); | ||
| break; | ||
| case "pacer:RateLimiter": | ||
| setStore({ | ||
| rateLimiters: updateOrAddToArray(store.rateLimiters, e.payload), | ||
| lastUpdatedByKey: { | ||
| ...store.lastUpdatedByKey, | ||
| [e.payload.key]: Date.now() | ||
| } | ||
| }); | ||
| break; | ||
| case "pacer:Throttler": | ||
| setStore({ | ||
| throttlers: updateOrAddToArray(store.throttlers, e.payload), | ||
| lastUpdatedByKey: { | ||
| ...store.lastUpdatedByKey, | ||
| [e.payload.key]: Date.now() | ||
| } | ||
| }); | ||
| break; | ||
| } | ||
| })); | ||
| }); | ||
| return createComponent(PacerDevtoolsContext.Provider, { | ||
| value: [store, setStore], | ||
| get children() { | ||
| return props.children; | ||
| } | ||
| }); | ||
| } | ||
| const usePacerDevtoolsContext = () => { | ||
| return useContext(PacerDevtoolsContext); | ||
| }; | ||
| const usePacerDevtoolsState = () => { | ||
| const [state] = usePacerDevtoolsContext(); | ||
| return state; | ||
| }; | ||
| //#endregion | ||
| //#region src/styles/tokens.ts | ||
| const tokens = { | ||
| colors: { | ||
| inherit: "inherit", | ||
| current: "currentColor", | ||
| transparent: "transparent", | ||
| black: "#000000", | ||
| white: "#ffffff", | ||
| neutral: { | ||
| 50: "#f9fafb", | ||
| 100: "#f2f4f7", | ||
| 200: "#eaecf0", | ||
| 300: "#d0d5dd", | ||
| 400: "#98a2b3", | ||
| 500: "#667085", | ||
| 600: "#475467", | ||
| 700: "#344054", | ||
| 800: "#1d2939", | ||
| 900: "#101828" | ||
| }, | ||
| darkGray: { | ||
| 50: "#525c7a", | ||
| 100: "#49536e", | ||
| 200: "#414962", | ||
| 300: "#394056", | ||
| 400: "#313749", | ||
| 500: "#292e3d", | ||
| 600: "#212530", | ||
| 700: "#191c24", | ||
| 800: "#111318", | ||
| 900: "#0b0d10" | ||
| }, | ||
| gray: { | ||
| 50: "#f9fafb", | ||
| 100: "#f2f4f7", | ||
| 200: "#eaecf0", | ||
| 300: "#d0d5dd", | ||
| 400: "#98a2b3", | ||
| 500: "#667085", | ||
| 600: "#475467", | ||
| 700: "#344054", | ||
| 800: "#1d2939", | ||
| 900: "#101828" | ||
| }, | ||
| blue: { | ||
| 25: "#F5FAFF", | ||
| 50: "#EFF8FF", | ||
| 100: "#D1E9FF", | ||
| 200: "#B2DDFF", | ||
| 300: "#84CAFF", | ||
| 400: "#53B1FD", | ||
| 500: "#2E90FA", | ||
| 600: "#1570EF", | ||
| 700: "#175CD3", | ||
| 800: "#1849A9", | ||
| 900: "#194185" | ||
| }, | ||
| green: { | ||
| 25: "#F6FEF9", | ||
| 50: "#ECFDF3", | ||
| 100: "#D1FADF", | ||
| 200: "#A6F4C5", | ||
| 300: "#6CE9A6", | ||
| 400: "#32D583", | ||
| 500: "#12B76A", | ||
| 600: "#039855", | ||
| 700: "#027A48", | ||
| 800: "#05603A", | ||
| 900: "#054F31" | ||
| }, | ||
| red: { | ||
| 50: "#fef2f2", | ||
| 100: "#fee2e2", | ||
| 200: "#fecaca", | ||
| 300: "#fca5a5", | ||
| 400: "#f87171", | ||
| 500: "#ef4444", | ||
| 600: "#dc2626", | ||
| 700: "#b91c1c", | ||
| 800: "#991b1b", | ||
| 900: "#7f1d1d", | ||
| 950: "#450a0a" | ||
| }, | ||
| yellow: { | ||
| 25: "#FFFCF5", | ||
| 50: "#FFFAEB", | ||
| 100: "#FEF0C7", | ||
| 200: "#FEDF89", | ||
| 300: "#FEC84B", | ||
| 400: "#FDB022", | ||
| 500: "#F79009", | ||
| 600: "#DC6803", | ||
| 700: "#B54708", | ||
| 800: "#93370D", | ||
| 900: "#7A2E0E" | ||
| }, | ||
| purple: { | ||
| 25: "#FAFAFF", | ||
| 50: "#F4F3FF", | ||
| 100: "#EBE9FE", | ||
| 200: "#D9D6FE", | ||
| 300: "#BDB4FE", | ||
| 400: "#9B8AFB", | ||
| 500: "#7A5AF8", | ||
| 600: "#6938EF", | ||
| 700: "#5925DC", | ||
| 800: "#4A1FB8", | ||
| 900: "#3E1C96" | ||
| }, | ||
| teal: { | ||
| 25: "#F6FEFC", | ||
| 50: "#F0FDF9", | ||
| 100: "#CCFBEF", | ||
| 200: "#99F6E0", | ||
| 300: "#5FE9D0", | ||
| 400: "#2ED3B7", | ||
| 500: "#15B79E", | ||
| 600: "#0E9384", | ||
| 700: "#107569", | ||
| 800: "#125D56", | ||
| 900: "#134E48" | ||
| }, | ||
| pink: { | ||
| 25: "#fdf2f8", | ||
| 50: "#fce7f3", | ||
| 100: "#fbcfe8", | ||
| 200: "#f9a8d4", | ||
| 300: "#f472b6", | ||
| 400: "#ec4899", | ||
| 500: "#db2777", | ||
| 600: "#be185d", | ||
| 700: "#9d174d", | ||
| 800: "#831843", | ||
| 900: "#500724" | ||
| }, | ||
| cyan: { | ||
| 25: "#ecfeff", | ||
| 50: "#cffafe", | ||
| 100: "#a5f3fc", | ||
| 200: "#67e8f9", | ||
| 300: "#22d3ee", | ||
| 400: "#06b6d4", | ||
| 500: "#0891b2", | ||
| 600: "#0e7490", | ||
| 700: "#155e75", | ||
| 800: "#164e63", | ||
| 900: "#083344" | ||
| } | ||
| }, | ||
| alpha: { | ||
| 100: "ff", | ||
| 90: "e5", | ||
| 80: "cc", | ||
| 70: "b3", | ||
| 60: "99", | ||
| 50: "80", | ||
| 40: "66", | ||
| 30: "4d", | ||
| 20: "33", | ||
| 10: "1a", | ||
| 0: "00" | ||
| }, | ||
| font: { | ||
| size: { | ||
| "2xs": "calc(var(--tsrd-font-size) * 0.625)", | ||
| xs: "calc(var(--tsrd-font-size) * 0.75)", | ||
| sm: "calc(var(--tsrd-font-size) * 0.875)", | ||
| md: "var(--tsrd-font-size)", | ||
| lg: "calc(var(--tsrd-font-size) * 1.125)", | ||
| xl: "calc(var(--tsrd-font-size) * 1.25)", | ||
| "2xl": "calc(var(--tsrd-font-size) * 1.5)", | ||
| "3xl": "calc(var(--tsrd-font-size) * 1.875)", | ||
| "4xl": "calc(var(--tsrd-font-size) * 2.25)", | ||
| "5xl": "calc(var(--tsrd-font-size) * 3)", | ||
| "6xl": "calc(var(--tsrd-font-size) * 3.75)", | ||
| "7xl": "calc(var(--tsrd-font-size) * 4.5)", | ||
| "8xl": "calc(var(--tsrd-font-size) * 6)", | ||
| "9xl": "calc(var(--tsrd-font-size) * 8)" | ||
| }, | ||
| lineHeight: { | ||
| "3xs": "calc(var(--tsrd-font-size) * 0.75)", | ||
| "2xs": "calc(var(--tsrd-font-size) * 0.875)", | ||
| xs: "calc(var(--tsrd-font-size) * 1)", | ||
| sm: "calc(var(--tsrd-font-size) * 1.25)", | ||
| md: "calc(var(--tsrd-font-size) * 1.5)", | ||
| lg: "calc(var(--tsrd-font-size) * 1.75)", | ||
| xl: "calc(var(--tsrd-font-size) * 2)", | ||
| "2xl": "calc(var(--tsrd-font-size) * 2.25)", | ||
| "3xl": "calc(var(--tsrd-font-size) * 2.5)", | ||
| "4xl": "calc(var(--tsrd-font-size) * 2.75)", | ||
| "5xl": "calc(var(--tsrd-font-size) * 3)", | ||
| "6xl": "calc(var(--tsrd-font-size) * 3.25)", | ||
| "7xl": "calc(var(--tsrd-font-size) * 3.5)", | ||
| "8xl": "calc(var(--tsrd-font-size) * 3.75)", | ||
| "9xl": "calc(var(--tsrd-font-size) * 4)" | ||
| }, | ||
| weight: { | ||
| thin: "100", | ||
| extralight: "200", | ||
| light: "300", | ||
| normal: "400", | ||
| medium: "500", | ||
| semibold: "600", | ||
| bold: "700", | ||
| extrabold: "800", | ||
| black: "900" | ||
| }, | ||
| fontFamily: { | ||
| sans: "ui-sans-serif, Inter, system-ui, sans-serif, sans-serif", | ||
| mono: `ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace` | ||
| } | ||
| }, | ||
| breakpoints: { | ||
| xs: "320px", | ||
| sm: "640px", | ||
| md: "768px", | ||
| lg: "1024px", | ||
| xl: "1280px", | ||
| "2xl": "1536px" | ||
| }, | ||
| border: { radius: { | ||
| none: "0px", | ||
| xs: "calc(var(--tsrd-font-size) * 0.125)", | ||
| sm: "calc(var(--tsrd-font-size) * 0.25)", | ||
| md: "calc(var(--tsrd-font-size) * 0.375)", | ||
| lg: "calc(var(--tsrd-font-size) * 0.5)", | ||
| xl: "calc(var(--tsrd-font-size) * 0.75)", | ||
| "2xl": "calc(var(--tsrd-font-size) * 1)", | ||
| "3xl": "calc(var(--tsrd-font-size) * 1.5)", | ||
| full: "9999px" | ||
| } }, | ||
| size: { | ||
| 0: "0px", | ||
| .25: "calc(var(--tsrd-font-size) * 0.0625)", | ||
| .5: "calc(var(--tsrd-font-size) * 0.125)", | ||
| 1: "calc(var(--tsrd-font-size) * 0.25)", | ||
| 1.5: "calc(var(--tsrd-font-size) * 0.375)", | ||
| 2: "calc(var(--tsrd-font-size) * 0.5)", | ||
| 2.5: "calc(var(--tsrd-font-size) * 0.625)", | ||
| 3: "calc(var(--tsrd-font-size) * 0.75)", | ||
| 3.5: "calc(var(--tsrd-font-size) * 0.875)", | ||
| 4: "calc(var(--tsrd-font-size) * 1)", | ||
| 4.5: "calc(var(--tsrd-font-size) * 1.125)", | ||
| 5: "calc(var(--tsrd-font-size) * 1.25)", | ||
| 5.5: "calc(var(--tsrd-font-size) * 1.375)", | ||
| 6: "calc(var(--tsrd-font-size) * 1.5)", | ||
| 6.5: "calc(var(--tsrd-font-size) * 1.625)", | ||
| 7: "calc(var(--tsrd-font-size) * 1.75)", | ||
| 8: "calc(var(--tsrd-font-size) * 2)", | ||
| 9: "calc(var(--tsrd-font-size) * 2.25)", | ||
| 10: "calc(var(--tsrd-font-size) * 2.5)", | ||
| 11: "calc(var(--tsrd-font-size) * 2.75)", | ||
| 12: "calc(var(--tsrd-font-size) * 3)", | ||
| 14: "calc(var(--tsrd-font-size) * 3.5)", | ||
| 16: "calc(var(--tsrd-font-size) * 4)", | ||
| 20: "calc(var(--tsrd-font-size) * 5)", | ||
| 24: "calc(var(--tsrd-font-size) * 6)", | ||
| 28: "calc(var(--tsrd-font-size) * 7)", | ||
| 32: "calc(var(--tsrd-font-size) * 8)", | ||
| 36: "calc(var(--tsrd-font-size) * 9)", | ||
| 40: "calc(var(--tsrd-font-size) * 10)", | ||
| 44: "calc(var(--tsrd-font-size) * 11)", | ||
| 48: "calc(var(--tsrd-font-size) * 12)", | ||
| 52: "calc(var(--tsrd-font-size) * 13)", | ||
| 56: "calc(var(--tsrd-font-size) * 14)", | ||
| 60: "calc(var(--tsrd-font-size) * 15)", | ||
| 64: "calc(var(--tsrd-font-size) * 16)", | ||
| 72: "calc(var(--tsrd-font-size) * 18)", | ||
| 80: "calc(var(--tsrd-font-size) * 20)", | ||
| 96: "calc(var(--tsrd-font-size) * 24)" | ||
| }, | ||
| shadow: { | ||
| xs: (_ = "rgb(0 0 0 / 0.1)") => `0 1px 2px 0 rgb(0 0 0 / 0.05)`, | ||
| sm: (color = "rgb(0 0 0 / 0.1)") => `0 1px 3px 0 ${color}, 0 1px 2px -1px ${color}`, | ||
| md: (color = "rgb(0 0 0 / 0.1)") => `0 4px 6px -1px ${color}, 0 2px 4px -2px ${color}`, | ||
| lg: (color = "rgb(0 0 0 / 0.1)") => `0 10px 15px -3px ${color}, 0 4px 6px -4px ${color}`, | ||
| xl: (color = "rgb(0 0 0 / 0.1)") => `0 20px 25px -5px ${color}, 0 8px 10px -6px ${color}`, | ||
| "2xl": (color = "rgb(0 0 0 / 0.25)") => `0 25px 50px -12px ${color}`, | ||
| inner: (color = "rgb(0 0 0 / 0.05)") => `inset 0 2px 4px 0 ${color}`, | ||
| none: () => `none` | ||
| }, | ||
| zIndices: { | ||
| hide: -1, | ||
| auto: "auto", | ||
| base: 0, | ||
| docked: 10, | ||
| dropdown: 1e3, | ||
| sticky: 1100, | ||
| banner: 1200, | ||
| overlay: 1300, | ||
| modal: 1400, | ||
| popover: 1500, | ||
| skipLink: 1600, | ||
| toast: 1700, | ||
| tooltip: 1800 | ||
| } | ||
| }; | ||
| //#endregion | ||
| //#region src/styles/use-styles.ts | ||
| const stylesFactory = (theme) => { | ||
| const { colors, font, size, alpha, border } = tokens; | ||
| const { fontFamily, size: fontSize } = font; | ||
| const css = goober.css; | ||
| const t = (light, dark) => theme === "light" ? light : dark; | ||
| return { | ||
| mainContainer: css` | ||
| display: flex; | ||
| flex: 1; | ||
| min-height: 0; | ||
| overflow: hidden; | ||
| padding: ${size[2]}; | ||
| padding-top: 0; | ||
| margin-top: ${size[2]}; | ||
| `, | ||
| dragHandle: css` | ||
| width: 8px; | ||
| background: ${t(colors.gray[300], colors.darkGray[600])}; | ||
| cursor: col-resize; | ||
| position: relative; | ||
| transition: all 0.2s ease; | ||
| user-select: none; | ||
| pointer-events: all; | ||
| margin: 0 ${size[1]}; | ||
| border-radius: 2px; | ||
| &:hover { | ||
| background: ${t(colors.blue[600], colors.blue[500])}; | ||
| margin: 0 ${size[1]}; | ||
| } | ||
| &.dragging { | ||
| background: ${t(colors.blue[700], colors.blue[600])}; | ||
| margin: 0 ${size[1]}; | ||
| } | ||
| &::after { | ||
| content: ''; | ||
| position: absolute; | ||
| top: 50%; | ||
| left: 50%; | ||
| transform: translate(-50%, -50%); | ||
| width: 2px; | ||
| height: 20px; | ||
| background: ${t(colors.gray[400], colors.darkGray[400])}; | ||
| border-radius: 1px; | ||
| pointer-events: none; | ||
| } | ||
| &:hover::after, | ||
| &.dragging::after { | ||
| background: ${t(colors.blue[500], colors.blue[300])}; | ||
| } | ||
| `, | ||
| leftPanel: css` | ||
| background: ${t(colors.gray[100], colors.darkGray[800])}; | ||
| border-radius: ${border.radius.lg}; | ||
| border: 1px solid ${t(colors.gray[200], colors.darkGray[700])}; | ||
| display: flex; | ||
| flex-direction: column; | ||
| overflow: hidden; | ||
| min-height: 0; | ||
| flex-shrink: 0; | ||
| `, | ||
| rightPanel: css` | ||
| background: ${t(colors.gray[100], colors.darkGray[800])}; | ||
| border-radius: ${border.radius.lg}; | ||
| border: 1px solid ${t(colors.gray[200], colors.darkGray[700])}; | ||
| display: flex; | ||
| flex-direction: column; | ||
| overflow: hidden; | ||
| min-height: 0; | ||
| flex: 1; | ||
| `, | ||
| panelHeader: css` | ||
| font-size: ${fontSize.md}; | ||
| font-weight: ${font.weight.bold}; | ||
| color: ${t(colors.blue[700], colors.blue[400])}; | ||
| padding: ${size[2]}; | ||
| border-bottom: 1px solid ${t(colors.gray[200], colors.darkGray[700])}; | ||
| background: ${t(colors.gray[100], colors.darkGray[800])}; | ||
| flex-shrink: 0; | ||
| `, | ||
| utilList: css` | ||
| flex: 1; | ||
| overflow-y: auto; | ||
| padding: ${size[1]}; | ||
| min-height: 0; | ||
| `, | ||
| utilGroup: css` | ||
| margin-bottom: ${size[2]}; | ||
| `, | ||
| utilGroupHeader: css` | ||
| font-size: ${fontSize.xs}; | ||
| font-weight: ${font.weight.semibold}; | ||
| color: ${t(colors.gray[600], colors.gray[400])}; | ||
| text-transform: uppercase; | ||
| letter-spacing: 0.05em; | ||
| margin-bottom: ${size[1]}; | ||
| padding: ${size[1]} ${size[2]}; | ||
| background: ${t(colors.gray[200], colors.darkGray[700])}; | ||
| border-radius: ${border.radius.md}; | ||
| `, | ||
| utilRow: css` | ||
| display: flex; | ||
| justify-content: space-between; | ||
| align-items: center; | ||
| padding: ${size[2]}; | ||
| margin-bottom: ${size[1]}; | ||
| background: ${t(colors.gray[200], colors.darkGray[700])}; | ||
| border-radius: ${border.radius.md}; | ||
| cursor: pointer; | ||
| transition: all 0.2s ease; | ||
| border: 1px solid transparent; | ||
| &:hover { | ||
| background: ${t(colors.gray[300], colors.darkGray[600])}; | ||
| border-color: ${t(colors.gray[400], colors.darkGray[500])}; | ||
| } | ||
| `, | ||
| utilRowSelected: css` | ||
| background: ${t(colors.blue[100], colors.blue[900] + alpha[20])}; | ||
| border-color: ${t(colors.blue[600], colors.blue[500])}; | ||
| box-shadow: 0 0 0 1px | ||
| ${t(colors.blue[600] + alpha[30], colors.blue[500] + alpha[30])}; | ||
| `, | ||
| utilKey: css` | ||
| font-family: ${fontFamily.mono}; | ||
| font-size: ${fontSize.xs}; | ||
| color: ${t(colors.gray[900], colors.gray[100])}; | ||
| flex: 1; | ||
| overflow: hidden; | ||
| text-overflow: ellipsis; | ||
| white-space: nowrap; | ||
| `, | ||
| utilStatus: css` | ||
| font-size: ${fontSize.xs}; | ||
| color: ${t(colors.gray[600], colors.gray[400])}; | ||
| text-transform: uppercase; | ||
| letter-spacing: 0.05em; | ||
| padding: ${size[1]} ${size[1]}; | ||
| background: ${t(colors.gray[300], colors.darkGray[600])}; | ||
| border-radius: ${border.radius.sm}; | ||
| margin-left: ${size[1]}; | ||
| `, | ||
| stateDetails: css` | ||
| flex: 1; | ||
| overflow-y: auto; | ||
| padding: ${size[2]}; | ||
| min-height: 0; | ||
| `, | ||
| stateHeader: css` | ||
| margin-bottom: ${size[2]}; | ||
| padding-bottom: ${size[2]}; | ||
| border-bottom: 1px solid ${t(colors.gray[200], colors.darkGray[700])}; | ||
| `, | ||
| stateTitle: css` | ||
| font-size: ${fontSize.md}; | ||
| font-weight: ${font.weight.bold}; | ||
| color: ${t(colors.blue[700], colors.blue[400])}; | ||
| margin-bottom: ${size[1]}; | ||
| `, | ||
| stateKey: css` | ||
| font-family: ${fontFamily.mono}; | ||
| font-size: ${fontSize.xs}; | ||
| color: ${t(colors.gray[600], colors.gray[400])}; | ||
| word-break: break-all; | ||
| `, | ||
| stateContent: css` | ||
| background: ${t(colors.gray[100], colors.darkGray[700])}; | ||
| border-radius: ${border.radius.md}; | ||
| padding: ${size[2]}; | ||
| border: 1px solid ${t(colors.gray[300], colors.darkGray[600])}; | ||
| `, | ||
| detailsGrid: css` | ||
| display: grid; | ||
| grid-template-columns: 1fr; | ||
| gap: ${size[2]}; | ||
| align-items: start; | ||
| `, | ||
| detailSection: css` | ||
| background: ${t(colors.white, colors.darkGray[700])}; | ||
| border: 1px solid ${t(colors.gray[300], colors.darkGray[600])}; | ||
| border-radius: ${border.radius.md}; | ||
| padding: ${size[2]}; | ||
| `, | ||
| detailSectionHeader: css` | ||
| font-size: ${fontSize.sm}; | ||
| font-weight: ${font.weight.bold}; | ||
| color: ${t(colors.gray[800], colors.gray[200])}; | ||
| margin-bottom: ${size[1]}; | ||
| text-transform: uppercase; | ||
| letter-spacing: 0.04em; | ||
| `, | ||
| actionsRow: css` | ||
| display: flex; | ||
| flex-wrap: wrap; | ||
| gap: ${size[2]}; | ||
| `, | ||
| actionButton: css` | ||
| display: inline-flex; | ||
| align-items: center; | ||
| gap: ${size[1]}; | ||
| padding: ${size[1]} ${size[2]}; | ||
| border-radius: ${border.radius.md}; | ||
| border: 1px solid ${t(colors.gray[300], colors.darkGray[500])}; | ||
| background: ${t(colors.gray[200], colors.darkGray[600])}; | ||
| color: ${t(colors.gray[900], colors.gray[100])}; | ||
| font-size: ${fontSize.xs}; | ||
| cursor: pointer; | ||
| user-select: none; | ||
| transition: | ||
| background 0.15s, | ||
| border-color 0.15s; | ||
| &:hover { | ||
| background: ${t(colors.gray[300], colors.darkGray[500])}; | ||
| border-color: ${t(colors.gray[400], colors.darkGray[400])}; | ||
| } | ||
| &:disabled { | ||
| opacity: 0.5; | ||
| cursor: not-allowed; | ||
| &:hover { | ||
| background: ${t(colors.gray[200], colors.darkGray[600])}; | ||
| border-color: ${t(colors.gray[300], colors.darkGray[500])}; | ||
| } | ||
| } | ||
| `, | ||
| actionDotBlue: css` | ||
| width: 6px; | ||
| height: 6px; | ||
| border-radius: 9999px; | ||
| background: ${colors.blue[400]}; | ||
| `, | ||
| actionDotGreen: css` | ||
| width: 6px; | ||
| height: 6px; | ||
| border-radius: 9999px; | ||
| background: ${colors.green[400]}; | ||
| `, | ||
| actionDotRed: css` | ||
| width: 6px; | ||
| height: 6px; | ||
| border-radius: 9999px; | ||
| background: ${colors.red[400]}; | ||
| `, | ||
| actionDotYellow: css` | ||
| width: 6px; | ||
| height: 6px; | ||
| border-radius: 9999px; | ||
| background: ${colors.yellow[400]}; | ||
| `, | ||
| actionDotOrange: css` | ||
| width: 6px; | ||
| height: 6px; | ||
| border-radius: 9999px; | ||
| background: ${colors.pink[400]}; | ||
| `, | ||
| actionDotPurple: css` | ||
| width: 6px; | ||
| height: 6px; | ||
| border-radius: 9999px; | ||
| background: ${colors.purple[400]}; | ||
| `, | ||
| infoGrid: css` | ||
| display: grid; | ||
| grid-template-columns: auto 1fr; | ||
| gap: ${size[1]}; | ||
| row-gap: ${size[1]}; | ||
| align-items: center; | ||
| `, | ||
| infoLabel: css` | ||
| color: ${t(colors.gray[600], colors.gray[400])}; | ||
| font-size: ${fontSize.xs}; | ||
| text-transform: uppercase; | ||
| letter-spacing: 0.05em; | ||
| `, | ||
| infoValueMono: css` | ||
| font-family: ${fontFamily.mono}; | ||
| font-size: ${fontSize.xs}; | ||
| color: ${t(colors.gray[900], colors.gray[100])}; | ||
| word-break: break-all; | ||
| `, | ||
| noSelection: css` | ||
| flex: 1; | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: center; | ||
| color: ${t(colors.gray[500], colors.gray[500])}; | ||
| font-style: italic; | ||
| text-align: center; | ||
| padding: ${size[4]}; | ||
| `, | ||
| sectionContainer: css` | ||
| display: flex; | ||
| flex-wrap: wrap; | ||
| gap: ${size[4]}; | ||
| `, | ||
| section: css` | ||
| background: ${t(colors.gray[100], colors.darkGray[800])}; | ||
| border-radius: ${border.radius.lg}; | ||
| box-shadow: ${tokens.shadow.md(t(colors.gray[400] + alpha[80], colors.black + alpha[80]))}; | ||
| padding: ${size[4]}; | ||
| margin-bottom: ${size[4]}; | ||
| border: 1px solid ${t(colors.gray[200], colors.darkGray[700])}; | ||
| min-width: 0; | ||
| max-width: 33%; | ||
| max-height: fit-content; | ||
| `, | ||
| sectionHeader: css` | ||
| font-size: ${fontSize.lg}; | ||
| font-weight: ${font.weight.bold}; | ||
| margin-bottom: ${size[2]}; | ||
| color: ${t(colors.blue[600], colors.blue[400])}; | ||
| letter-spacing: 0.01em; | ||
| display: flex; | ||
| align-items: center; | ||
| gap: ${size[2]}; | ||
| `, | ||
| sectionEmpty: css` | ||
| color: ${t(colors.gray[500], colors.gray[500])}; | ||
| font-size: ${fontSize.sm}; | ||
| font-style: italic; | ||
| margin: ${size[2]} 0; | ||
| `, | ||
| instanceList: css` | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: ${size[2]}; | ||
| background: ${t(colors.gray[200], colors.darkGray[700])}; | ||
| border: 1px solid ${t(colors.gray[300], colors.darkGray[600])}; | ||
| `, | ||
| instanceCard: css` | ||
| background: ${t(colors.gray[200], colors.darkGray[700])}; | ||
| border-radius: ${border.radius.md}; | ||
| padding: ${size[3]}; | ||
| border: 1px solid ${t(colors.gray[300], colors.darkGray[600])}; | ||
| font-size: ${fontSize.sm}; | ||
| color: ${t(colors.gray[900], colors.gray[100])}; | ||
| font-family: ${fontFamily.mono}; | ||
| overflow-x: auto; | ||
| transition: | ||
| box-shadow 0.3s, | ||
| background 0.3s; | ||
| ` | ||
| }; | ||
| }; | ||
| function useStyles() { | ||
| const { theme } = useTheme(); | ||
| const [styles, setStyles] = createSignal(stylesFactory(theme())); | ||
| createEffect(() => { | ||
| setStyles(stylesFactory(theme())); | ||
| }); | ||
| return styles; | ||
| } | ||
| //#endregion | ||
| //#region src/components/util-groups.ts | ||
| const UTIL_GROUPS = [ | ||
| { | ||
| key: "debouncers", | ||
| label: "Debouncers", | ||
| displayName: "Debouncer" | ||
| }, | ||
| { | ||
| key: "throttlers", | ||
| label: "Throttlers", | ||
| displayName: "Throttler" | ||
| }, | ||
| { | ||
| key: "batchers", | ||
| label: "Batchers", | ||
| displayName: "Batcher" | ||
| }, | ||
| { | ||
| key: "queuers", | ||
| label: "Queuers", | ||
| displayName: "Queuer" | ||
| }, | ||
| { | ||
| key: "rateLimiters", | ||
| label: "Rate Limiters", | ||
| displayName: "Rate Limiter" | ||
| }, | ||
| { | ||
| key: "asyncDebouncers", | ||
| label: "Async Debouncers", | ||
| displayName: "Async Debouncer" | ||
| }, | ||
| { | ||
| key: "asyncThrottlers", | ||
| label: "Async Throttlers", | ||
| displayName: "Async Throttler" | ||
| }, | ||
| { | ||
| key: "asyncBatchers", | ||
| label: "Async Batchers", | ||
| displayName: "Async Batcher" | ||
| }, | ||
| { | ||
| key: "asyncQueuers", | ||
| label: "Async Queuers", | ||
| displayName: "Async Queuer" | ||
| }, | ||
| { | ||
| key: "asyncRateLimiters", | ||
| label: "Async Rate Limiters", | ||
| displayName: "Async Rate Limiter" | ||
| } | ||
| ]; | ||
| //#endregion | ||
| //#region src/components/UtilList.tsx | ||
| var _tmpl$$4 = /* @__PURE__ */ template(`<div>`), _tmpl$2$2 = /* @__PURE__ */ template(`<div><div>`), _tmpl$3$2 = /* @__PURE__ */ template(`<div><div></div><div>`); | ||
| function UtilList(props) { | ||
| const styles = useStyles(); | ||
| return (() => { | ||
| var _el$ = _tmpl$$4(); | ||
| insert(_el$, createComponent(For, { | ||
| each: UTIL_GROUPS, | ||
| children: (group) => memo(() => memo(() => props.utilState()[group.key].length > 0)() && (() => { | ||
| var _el$2 = _tmpl$2$2(), _el$3 = _el$2.firstChild; | ||
| insert(_el$3, () => group.label); | ||
| insert(_el$2, createComponent(For, { | ||
| get each() { | ||
| return props.utilState()[group.key]; | ||
| }, | ||
| children: (instance) => { | ||
| const status = (() => { | ||
| try { | ||
| const statusAccessor = useStore(instance.store, (s) => s.status); | ||
| return () => statusAccessor() ?? "unknown"; | ||
| } catch { | ||
| return () => "unknown"; | ||
| } | ||
| })(); | ||
| return (() => { | ||
| var _el$4 = _tmpl$3$2(), _el$5 = _el$4.firstChild, _el$6 = _el$5.nextSibling; | ||
| _el$4.$$click = () => props.setSelectedKey(instance.key ?? null); | ||
| insert(_el$5, () => instance.key); | ||
| insert(_el$6, status); | ||
| effect((_p$) => { | ||
| var _v$3 = clsx(styles().utilRow, props.selectedKey() === instance.key && styles().utilRowSelected), _v$4 = styles().utilKey, _v$5 = styles().utilStatus; | ||
| _v$3 !== _p$.e && className(_el$4, _p$.e = _v$3); | ||
| _v$4 !== _p$.t && className(_el$5, _p$.t = _v$4); | ||
| _v$5 !== _p$.a && className(_el$6, _p$.a = _v$5); | ||
| return _p$; | ||
| }, { | ||
| e: void 0, | ||
| t: void 0, | ||
| a: void 0 | ||
| }); | ||
| return _el$4; | ||
| })(); | ||
| } | ||
| }), null); | ||
| effect((_p$) => { | ||
| var _v$ = styles().utilGroup, _v$2 = styles().utilGroupHeader; | ||
| _v$ !== _p$.e && className(_el$2, _p$.e = _v$); | ||
| _v$2 !== _p$.t && className(_el$3, _p$.t = _v$2); | ||
| return _p$; | ||
| }, { | ||
| e: void 0, | ||
| t: void 0 | ||
| }); | ||
| return _el$2; | ||
| })()) | ||
| })); | ||
| effect(() => className(_el$, styles().utilList)); | ||
| return _el$; | ||
| })(); | ||
| } | ||
| delegateEvents(["click"]); | ||
| //#endregion | ||
| //#region src/components/ActionButtons.tsx | ||
| var _tmpl$$3 = /* @__PURE__ */ template(`<div>No actions available for this util`), _tmpl$2$1 = /* @__PURE__ */ template(`<div>`), _tmpl$3$1 = /* @__PURE__ */ template(`<button><span>`), _tmpl$4 = /* @__PURE__ */ template(`<button><span></span>Flush`), _tmpl$5 = /* @__PURE__ */ template(`<button><span></span>Cancel`), _tmpl$6 = /* @__PURE__ */ template(`<button><span></span>Reset`), _tmpl$7 = /* @__PURE__ */ template(`<button><span></span>Clear`); | ||
| function ActionButtons(props) { | ||
| const styles = useStyles(); | ||
| const utilInstance = props.instance; | ||
| const utilState = utilInstance?.store?.state; | ||
| const hasPending = utilState && "isPending" in utilState; | ||
| const hasEmpty = utilState && "isEmpty" in utilState; | ||
| const isPending = hasPending ? !!utilState.isPending : false; | ||
| const isEmpty = hasEmpty ? utilState.isEmpty : void 0; | ||
| const hasFlush = typeof utilInstance.flush === "function"; | ||
| const hasCancel = typeof utilInstance.cancel === "function"; | ||
| const hasReset = typeof utilInstance.reset === "function"; | ||
| const hasClear = typeof utilInstance.clear === "function"; | ||
| const hasStart = typeof utilInstance.start === "function"; | ||
| const hasStop = typeof utilInstance.stop === "function"; | ||
| const hasStartStop = hasStart && hasStop; | ||
| const isRunning = utilState?.isRunning ?? true; | ||
| if (!hasPending && !hasFlush && !hasCancel && !hasReset && !hasClear) return (() => { | ||
| var _el$ = _tmpl$$3(); | ||
| effect(() => className(_el$, styles().sectionEmpty)); | ||
| return _el$; | ||
| })(); | ||
| const emitName = `d-${props.utilName}`; | ||
| return (() => { | ||
| var _el$2 = _tmpl$2$1(); | ||
| insert(_el$2, hasPending && (() => { | ||
| var _el$3 = _tmpl$3$1(), _el$4 = _el$3.firstChild; | ||
| _el$3.$$mousedown = () => { | ||
| const next = !isPending; | ||
| utilInstance.store.setState((prev) => ({ | ||
| ...prev, | ||
| isPending: next | ||
| })); | ||
| pacerEventClient$1.emit(emitName, utilInstance); | ||
| }; | ||
| insert(_el$3, isPending ? "Restore Pending" : "Trigger Pending", null); | ||
| effect((_p$) => { | ||
| var _v$ = styles().actionButton, _v$2 = styles().actionDotBlue; | ||
| _v$ !== _p$.e && className(_el$3, _p$.e = _v$); | ||
| _v$2 !== _p$.t && className(_el$4, _p$.t = _v$2); | ||
| return _p$; | ||
| }, { | ||
| e: void 0, | ||
| t: void 0 | ||
| }); | ||
| return _el$3; | ||
| })(), null); | ||
| insert(_el$2, hasFlush && (() => { | ||
| var _el$5 = _tmpl$4(), _el$6 = _el$5.firstChild; | ||
| _el$5.$$mousedown = () => { | ||
| utilInstance.flush(); | ||
| pacerEventClient$1.emit(emitName, utilInstance); | ||
| }; | ||
| effect((_p$) => { | ||
| var _v$3 = styles().actionButton, _v$4 = props.utilName.toLowerCase().includes("debouncer") && !isPending || props.utilName.toLowerCase().includes("throttler") && !isPending || props.utilName.toLowerCase().includes("batcher") && isEmpty || props.utilName.toLowerCase().includes("queuer") && isEmpty, _v$5 = styles().actionDotGreen; | ||
| _v$3 !== _p$.e && className(_el$5, _p$.e = _v$3); | ||
| _v$4 !== _p$.t && (_el$5.disabled = _p$.t = _v$4); | ||
| _v$5 !== _p$.a && className(_el$6, _p$.a = _v$5); | ||
| return _p$; | ||
| }, { | ||
| e: void 0, | ||
| t: void 0, | ||
| a: void 0 | ||
| }); | ||
| return _el$5; | ||
| })(), null); | ||
| insert(_el$2, hasCancel && (() => { | ||
| var _el$7 = _tmpl$5(), _el$8 = _el$7.firstChild; | ||
| _el$7.$$mousedown = () => { | ||
| utilInstance.cancel(); | ||
| pacerEventClient$1.emit(emitName, utilInstance); | ||
| }; | ||
| effect((_p$) => { | ||
| var _v$6 = styles().actionButton, _v$7 = props.utilName.toLowerCase().includes("debouncer") && !isPending || props.utilName.toLowerCase().includes("throttler") && !isPending || props.utilName.toLowerCase().includes("batcher") && isEmpty || props.utilName.toLowerCase().includes("queuer") && isEmpty, _v$8 = styles().actionDotRed; | ||
| _v$6 !== _p$.e && className(_el$7, _p$.e = _v$6); | ||
| _v$7 !== _p$.t && (_el$7.disabled = _p$.t = _v$7); | ||
| _v$8 !== _p$.a && className(_el$8, _p$.a = _v$8); | ||
| return _p$; | ||
| }, { | ||
| e: void 0, | ||
| t: void 0, | ||
| a: void 0 | ||
| }); | ||
| return _el$7; | ||
| })(), null); | ||
| insert(_el$2, hasReset && (() => { | ||
| var _el$9 = _tmpl$6(), _el$0 = _el$9.firstChild; | ||
| _el$9.$$mousedown = () => { | ||
| utilInstance.reset(); | ||
| pacerEventClient$1.emit(emitName, utilInstance); | ||
| }; | ||
| effect((_p$) => { | ||
| var _v$9 = styles().actionButton, _v$0 = styles().actionDotYellow; | ||
| _v$9 !== _p$.e && className(_el$9, _p$.e = _v$9); | ||
| _v$0 !== _p$.t && className(_el$0, _p$.t = _v$0); | ||
| return _p$; | ||
| }, { | ||
| e: void 0, | ||
| t: void 0 | ||
| }); | ||
| return _el$9; | ||
| })(), null); | ||
| insert(_el$2, hasClear && (() => { | ||
| var _el$1 = _tmpl$7(), _el$10 = _el$1.firstChild; | ||
| _el$1.$$mousedown = () => { | ||
| utilInstance.clear(); | ||
| pacerEventClient$1.emit(emitName, utilInstance); | ||
| }; | ||
| effect((_p$) => { | ||
| var _v$1 = styles().actionButton, _v$10 = props.utilName.toLowerCase().includes("batcher") && isEmpty || props.utilName.toLowerCase().includes("queuer") && isEmpty, _v$11 = styles().actionDotOrange; | ||
| _v$1 !== _p$.e && className(_el$1, _p$.e = _v$1); | ||
| _v$10 !== _p$.t && (_el$1.disabled = _p$.t = _v$10); | ||
| _v$11 !== _p$.a && className(_el$10, _p$.a = _v$11); | ||
| return _p$; | ||
| }, { | ||
| e: void 0, | ||
| t: void 0, | ||
| a: void 0 | ||
| }); | ||
| return _el$1; | ||
| })(), null); | ||
| insert(_el$2, hasStartStop && (() => { | ||
| var _el$11 = _tmpl$3$1(), _el$12 = _el$11.firstChild; | ||
| _el$11.$$mousedown = () => { | ||
| if (isRunning) utilInstance.stop(); | ||
| else utilInstance.start(); | ||
| pacerEventClient$1.emit(emitName, utilInstance); | ||
| }; | ||
| insert(_el$11, isRunning ? "Stop" : "Start", null); | ||
| effect((_p$) => { | ||
| var _v$12 = styles().actionButton, _v$13 = styles().actionDotPurple; | ||
| _v$12 !== _p$.e && className(_el$11, _p$.e = _v$12); | ||
| _v$13 !== _p$.t && className(_el$12, _p$.t = _v$13); | ||
| return _p$; | ||
| }, { | ||
| e: void 0, | ||
| t: void 0 | ||
| }); | ||
| return _el$11; | ||
| })(), null); | ||
| effect(() => className(_el$2, styles().actionsRow)); | ||
| return _el$2; | ||
| })(); | ||
| } | ||
| delegateEvents(["mousedown"]); | ||
| //#endregion | ||
| //#region src/components/StateHeader.tsx | ||
| var _tmpl$$2 = /* @__PURE__ */ template(`<div><div></div><div style=display:flex;align-items:center;gap:16px><div><div>Key</div><div></div><div>Last Updated</div><div> (<!>)</div></div><div style=margin-left:auto;font-weight:bold>% reduction`); | ||
| dayjs.extend(relativeTime); | ||
| function StateHeader(props) { | ||
| const styles = useStyles(); | ||
| const [now, setNow] = createSignal(Date.now()); | ||
| onMount(() => { | ||
| const interval = setInterval(() => { | ||
| setNow(Date.now()); | ||
| }, 1e3); | ||
| onCleanup(() => { | ||
| clearInterval(interval); | ||
| }); | ||
| }); | ||
| const entry = props.selectedInstance(); | ||
| if (!entry) return null; | ||
| const key = entry.instance.key; | ||
| const updatedAt = props.utilState().lastUpdatedByKey[key] ?? Date.now(); | ||
| const getRelativeTime = () => { | ||
| const diffMs = now() - updatedAt; | ||
| const diffSeconds = Math.floor(diffMs / 1e3); | ||
| if (diffSeconds < 60) return `${diffSeconds} second${diffSeconds !== 1 ? "s" : ""} ago`; | ||
| return dayjs(updatedAt).fromNow(); | ||
| }; | ||
| const getReductionPercentage = () => { | ||
| const state = entry.instance.store?.state || entry.instance.state; | ||
| if (!state) return 0; | ||
| const completedExecutions = entry.type.toLowerCase().includes("async") ? state.settleCount || 0 : state.executionCount || 0; | ||
| if (entry.type.toLowerCase().includes("batcher")) { | ||
| const totalItemsProcessed = state.totalItemsProcessed || 0; | ||
| if (totalItemsProcessed === 0) return 0; | ||
| return Math.round((totalItemsProcessed - completedExecutions) / totalItemsProcessed * 100); | ||
| } | ||
| let requestCount = 0; | ||
| if (state.maybeExecuteCount !== void 0) requestCount = state.maybeExecuteCount; | ||
| else if (state.addItemCount !== void 0) requestCount = state.addItemCount; | ||
| else return 0; | ||
| if (requestCount === 0) return 0; | ||
| const reduction = requestCount - completedExecutions; | ||
| return Math.round(reduction / requestCount * 100); | ||
| }; | ||
| const reductionPercentage = getReductionPercentage(); | ||
| return (() => { | ||
| var _el$ = _tmpl$$2(), _el$2 = _el$.firstChild, _el$4 = _el$2.nextSibling.firstChild, _el$5 = _el$4.firstChild, _el$6 = _el$5.nextSibling, _el$7 = _el$6.nextSibling, _el$8 = _el$7.nextSibling, _el$9 = _el$8.firstChild, _el$1 = _el$9.nextSibling; | ||
| _el$1.nextSibling; | ||
| var _el$10 = _el$4.nextSibling, _el$11 = _el$10.firstChild; | ||
| insert(_el$2, () => entry.type); | ||
| insert(_el$6, key); | ||
| insert(_el$8, () => new Date(updatedAt).toLocaleTimeString(), _el$9); | ||
| insert(_el$8, getRelativeTime, _el$1); | ||
| insert(_el$10, reductionPercentage, _el$11); | ||
| effect((_p$) => { | ||
| var _v$ = styles().stateHeader, _v$2 = styles().stateTitle, _v$3 = styles().infoGrid, _v$4 = styles().infoLabel, _v$5 = styles().infoValueMono, _v$6 = styles().infoLabel, _v$7 = styles().infoValueMono, _v$8 = styles().infoValueMono; | ||
| _v$ !== _p$.e && className(_el$, _p$.e = _v$); | ||
| _v$2 !== _p$.t && className(_el$2, _p$.t = _v$2); | ||
| _v$3 !== _p$.a && className(_el$4, _p$.a = _v$3); | ||
| _v$4 !== _p$.o && className(_el$5, _p$.o = _v$4); | ||
| _v$5 !== _p$.i && className(_el$6, _p$.i = _v$5); | ||
| _v$6 !== _p$.n && className(_el$7, _p$.n = _v$6); | ||
| _v$7 !== _p$.s && className(_el$8, _p$.s = _v$7); | ||
| _v$8 !== _p$.h && className(_el$10, _p$.h = _v$8); | ||
| return _p$; | ||
| }, { | ||
| e: void 0, | ||
| t: void 0, | ||
| a: void 0, | ||
| o: void 0, | ||
| i: void 0, | ||
| n: void 0, | ||
| s: void 0, | ||
| h: void 0 | ||
| }); | ||
| return _el$; | ||
| })(); | ||
| } | ||
| //#endregion | ||
| //#region src/components/DetailsPanel.tsx | ||
| var _tmpl$$1 = /* @__PURE__ */ template(`<div>`), _tmpl$2 = /* @__PURE__ */ template(`<div>Select a util from the left panel to view its state`), _tmpl$3 = /* @__PURE__ */ template(`<div><div><div>Actions</div></div><div><div>State</div><div></div></div><div><div>Options</div><div>`); | ||
| function DetailsPanel(props) { | ||
| const styles = useStyles(); | ||
| return (() => { | ||
| var _el$ = _tmpl$$1(); | ||
| insert(_el$, () => { | ||
| const entry = props.selectedInstance(); | ||
| if (!entry) return (() => { | ||
| var _el$2 = _tmpl$2(); | ||
| effect(() => className(_el$2, styles().noSelection)); | ||
| return _el$2; | ||
| })(); | ||
| return [createComponent(StateHeader, { | ||
| get selectedInstance() { | ||
| return props.selectedInstance; | ||
| }, | ||
| get utilState() { | ||
| return props.utilState; | ||
| } | ||
| }), (() => { | ||
| var _el$3 = _tmpl$3(), _el$4 = _el$3.firstChild, _el$5 = _el$4.firstChild, _el$6 = _el$4.nextSibling, _el$7 = _el$6.firstChild, _el$8 = _el$7.nextSibling, _el$9 = _el$6.nextSibling, _el$0 = _el$9.firstChild, _el$1 = _el$0.nextSibling; | ||
| insert(_el$4, createComponent(ActionButtons, { | ||
| get instance() { | ||
| return entry.instance; | ||
| }, | ||
| get utilName() { | ||
| return entry.type; | ||
| } | ||
| }), null); | ||
| insert(_el$8, createComponent(JsonTree, { get value() { | ||
| return entry.instance.store?.state; | ||
| } })); | ||
| insert(_el$1, createComponent(JsonTree, { get value() { | ||
| return entry.instance.options; | ||
| } })); | ||
| effect((_p$) => { | ||
| var _v$ = styles().detailsGrid, _v$2 = styles().detailSection, _v$3 = styles().detailSectionHeader, _v$4 = styles().detailSection, _v$5 = styles().detailSectionHeader, _v$6 = styles().stateContent, _v$7 = styles().detailSection, _v$8 = styles().detailSectionHeader, _v$9 = styles().stateContent; | ||
| _v$ !== _p$.e && className(_el$3, _p$.e = _v$); | ||
| _v$2 !== _p$.t && className(_el$4, _p$.t = _v$2); | ||
| _v$3 !== _p$.a && className(_el$5, _p$.a = _v$3); | ||
| _v$4 !== _p$.o && className(_el$6, _p$.o = _v$4); | ||
| _v$5 !== _p$.i && className(_el$7, _p$.i = _v$5); | ||
| _v$6 !== _p$.n && className(_el$8, _p$.n = _v$6); | ||
| _v$7 !== _p$.s && className(_el$9, _p$.s = _v$7); | ||
| _v$8 !== _p$.h && className(_el$0, _p$.h = _v$8); | ||
| _v$9 !== _p$.r && className(_el$1, _p$.r = _v$9); | ||
| return _p$; | ||
| }, { | ||
| e: void 0, | ||
| t: void 0, | ||
| a: void 0, | ||
| o: void 0, | ||
| i: void 0, | ||
| n: void 0, | ||
| s: void 0, | ||
| h: void 0, | ||
| r: void 0 | ||
| }); | ||
| return _el$3; | ||
| })()]; | ||
| }); | ||
| effect(() => className(_el$, styles().stateDetails)); | ||
| return _el$; | ||
| })(); | ||
| } | ||
| //#endregion | ||
| //#region src/components/Shell.tsx | ||
| var _tmpl$ = /* @__PURE__ */ template(`<div><div style=min-width:150px;max-width:800px></div><div></div><div style=flex:1><div>Details`); | ||
| function Shell() { | ||
| const styles = useStyles(); | ||
| const state = usePacerDevtoolsState(); | ||
| const utilState = () => state; | ||
| const [selectedKey, setSelectedKey] = createSignal(null); | ||
| const [leftPanelWidth, setLeftPanelWidth] = createSignal(300); | ||
| const [isDragging, setIsDragging] = createSignal(false); | ||
| const selectedInstance = createMemo(() => { | ||
| const key = selectedKey(); | ||
| if (!key) return null; | ||
| for (const group of UTIL_GROUPS) { | ||
| const instance = utilState()[group.key].find((inst) => inst.key === key); | ||
| if (instance) return { | ||
| instance, | ||
| type: group.displayName | ||
| }; | ||
| } | ||
| return null; | ||
| }); | ||
| let dragStartX = 0; | ||
| let dragStartWidth = 0; | ||
| const handleMouseDown = (e) => { | ||
| e.preventDefault(); | ||
| e.stopPropagation(); | ||
| setIsDragging(true); | ||
| document.body.style.cursor = "col-resize"; | ||
| document.body.style.userSelect = "none"; | ||
| dragStartX = e.clientX; | ||
| dragStartWidth = leftPanelWidth(); | ||
| }; | ||
| const handleMouseMove = (e) => { | ||
| if (!isDragging()) return; | ||
| e.preventDefault(); | ||
| const deltaX = e.clientX - dragStartX; | ||
| setLeftPanelWidth(Math.max(150, Math.min(800, dragStartWidth + deltaX))); | ||
| }; | ||
| const handleMouseUp = () => { | ||
| setIsDragging(false); | ||
| document.body.style.cursor = ""; | ||
| document.body.style.userSelect = ""; | ||
| }; | ||
| onMount(() => { | ||
| document.addEventListener("mousemove", handleMouseMove); | ||
| document.addEventListener("mouseup", handleMouseUp); | ||
| }); | ||
| onCleanup(() => { | ||
| document.removeEventListener("mousemove", handleMouseMove); | ||
| document.removeEventListener("mouseup", handleMouseUp); | ||
| }); | ||
| return createComponent(MainPanel, { get children() { | ||
| return [createComponent(Header, { get children() { | ||
| return createComponent(HeaderLogo, { | ||
| flavor: { | ||
| light: "#84cc16", | ||
| dark: "#84cc16" | ||
| }, | ||
| children: "TanStack Pacer" | ||
| }); | ||
| } }), (() => { | ||
| var _el$ = _tmpl$(), _el$2 = _el$.firstChild, _el$3 = _el$2.nextSibling, _el$4 = _el$3.nextSibling, _el$5 = _el$4.firstChild; | ||
| insert(_el$2, createComponent(UtilList, { | ||
| selectedKey, | ||
| setSelectedKey, | ||
| utilState | ||
| })); | ||
| _el$3.$$mousedown = handleMouseDown; | ||
| insert(_el$4, createComponent(DetailsPanel, { | ||
| selectedInstance, | ||
| utilState | ||
| }), null); | ||
| effect((_p$) => { | ||
| var _v$ = styles().mainContainer, _v$2 = styles().leftPanel, _v$3 = `${leftPanelWidth()}px`, _v$4 = `${styles().dragHandle} ${isDragging() ? "dragging" : ""}`, _v$5 = styles().rightPanel, _v$6 = styles().panelHeader; | ||
| _v$ !== _p$.e && className(_el$, _p$.e = _v$); | ||
| _v$2 !== _p$.t && className(_el$2, _p$.t = _v$2); | ||
| _v$3 !== _p$.a && setStyleProperty(_el$2, "width", _p$.a = _v$3); | ||
| _v$4 !== _p$.o && className(_el$3, _p$.o = _v$4); | ||
| _v$5 !== _p$.i && className(_el$4, _p$.i = _v$5); | ||
| _v$6 !== _p$.n && className(_el$5, _p$.n = _v$6); | ||
| return _p$; | ||
| }, { | ||
| e: void 0, | ||
| t: void 0, | ||
| a: void 0, | ||
| o: void 0, | ||
| i: void 0, | ||
| n: void 0 | ||
| }); | ||
| return _el$; | ||
| })()]; | ||
| } }); | ||
| } | ||
| delegateEvents(["mousedown"]); | ||
| //#endregion | ||
| //#region src/components/index.tsx | ||
| function PacerDevtools(props) { | ||
| return createComponent(ThemeContextProvider, { | ||
| get theme() { | ||
| return props.theme; | ||
| }, | ||
| get children() { | ||
| return createComponent(PacerContextProvider, { get children() { | ||
| return createComponent(Shell, {}); | ||
| } }); | ||
| } | ||
| }); | ||
| } | ||
| //#endregion | ||
| export { PacerDevtools as default }; |
| import { ThemeContextProvider } from '@tanstack/devtools-ui' | ||
| import { PacerContextProvider } from '../PacerContextProvider' | ||
| import { Shell } from './Shell' | ||
| import type { TanStackDevtoolsTheme } from '@tanstack/devtools-ui' | ||
| interface DevtoolsProps { | ||
| theme: TanStackDevtoolsTheme | ||
| } | ||
| export default function PacerDevtools(props: DevtoolsProps) { | ||
| return ( | ||
| <ThemeContextProvider theme={props.theme}> | ||
| <PacerContextProvider> | ||
| <Shell /> | ||
| </PacerContextProvider> | ||
| </ThemeContextProvider> | ||
| ) | ||
| } |
+1
-1
| "use client"; | ||
| import { constructCoreClass } from "@tanstack/devtools-utils/solid"; | ||
| //#region src/core.tsx | ||
| const [PacerDevtoolsCore$1, PacerDevtoolsCoreNoOp] = constructCoreClass(() => import("./PacerDevtools-C_K6wJJQ.js")); | ||
| const [PacerDevtoolsCore$1, PacerDevtoolsCoreNoOp] = constructCoreClass(() => import("./components-DDmnxkL2.js")); | ||
| //#endregion | ||
@@ -6,0 +6,0 @@ //#region src/index.ts |
| "use client"; | ||
| import { constructCoreClass } from "@tanstack/devtools-utils/solid"; | ||
| //#region src/core.tsx | ||
| const [PacerDevtoolsCore, PacerDevtoolsCoreNoOp] = constructCoreClass(() => import("../PacerDevtools-C_K6wJJQ.js")); | ||
| const [PacerDevtoolsCore, PacerDevtoolsCoreNoOp] = constructCoreClass(() => import("../components-DDmnxkL2.js")); | ||
| //#endregion | ||
| export { PacerDevtoolsCore }; |
| "use client"; | ||
| import { constructCoreClass } from "@tanstack/devtools-utils/solid"; | ||
| //#region src/core.tsx | ||
| const [PacerDevtoolsCore, PacerDevtoolsCoreNoOp] = constructCoreClass(() => import("../PacerDevtools-DMClGMZE.js")); | ||
| const [PacerDevtoolsCore, PacerDevtoolsCoreNoOp] = constructCoreClass(() => import("../components-ByKT1JiI.js")); | ||
| //#endregion | ||
| export { PacerDevtoolsCore }; |
+1
-1
| "use client"; | ||
| import { constructCoreClass } from "@tanstack/devtools-utils/solid"; | ||
| //#region src/core.tsx | ||
| const [PacerDevtoolsCore$1, PacerDevtoolsCoreNoOp] = constructCoreClass(() => import("./PacerDevtools-DMClGMZE.js")); | ||
| const [PacerDevtoolsCore$1, PacerDevtoolsCoreNoOp] = constructCoreClass(() => import("./components-ByKT1JiI.js")); | ||
| //#endregion | ||
@@ -6,0 +6,0 @@ //#region src/index.ts |
+1
-1
| { | ||
| "name": "@tanstack/pacer-devtools", | ||
| "version": "1.1.4", | ||
| "version": "1.1.5", | ||
| "description": "Devtools for Pacer.", | ||
@@ -5,0 +5,0 @@ "author": "Tanner Linsley", |
+1
-1
@@ -6,5 +6,5 @@ import { constructCoreClass } from '@tanstack/devtools-utils/solid' | ||
| const [PacerDevtoolsCore, PacerDevtoolsCoreNoOp] = constructCoreClass( | ||
| () => import('./PacerDevtools'), | ||
| () => import('./components'), | ||
| ) | ||
| export { PacerDevtoolsCore, PacerDevtoolsCoreNoOp } |
| import { className, createComponent, delegateEvents, effect, insert, memo, setStyleProperty, template } from "solid-js/web"; | ||
| import { Header, HeaderLogo, JsonTree, MainPanel, ThemeContextProvider, useTheme } from "@tanstack/devtools-ui"; | ||
| import { createStore } from "solid-js/store"; | ||
| import { For, createContext, createEffect, createMemo, createSignal, onCleanup, onMount, useContext } from "solid-js"; | ||
| import { pacerEventClient } from "@tanstack/pacer/event-client"; | ||
| import * as goober from "goober"; | ||
| import { useStore } from "@tanstack/solid-store"; | ||
| import clsx from "clsx"; | ||
| import { pacerEventClient as pacerEventClient$1 } from "@tanstack/pacer"; | ||
| import dayjs from "dayjs"; | ||
| import relativeTime from "dayjs/plugin/relativeTime.js"; | ||
| //#region src/PacerContextProvider.tsx | ||
| const initialPacerDevtoolsStore = { | ||
| asyncBatchers: [], | ||
| asyncDebouncers: [], | ||
| asyncQueuers: [], | ||
| asyncRateLimiters: [], | ||
| asyncThrottlers: [], | ||
| batchers: [], | ||
| debouncers: [], | ||
| queuers: [], | ||
| rateLimiters: [], | ||
| throttlers: [], | ||
| lastUpdatedByKey: {} | ||
| }; | ||
| const PacerDevtoolsContext = createContext([initialPacerDevtoolsStore, () => {}]); | ||
| const updateOrAddToArray = (oldArray, newItem) => { | ||
| const index = oldArray.findIndex((item) => item.key === newItem.key); | ||
| if (index !== -1) return oldArray.map((item, i) => i === index ? newItem : item); | ||
| return [...oldArray, newItem]; | ||
| }; | ||
| function PacerContextProvider(props) { | ||
| const [store, setStore] = createStore(initialPacerDevtoolsStore); | ||
| createEffect(() => { | ||
| onCleanup(pacerEventClient.onAllPluginEvents((_e) => { | ||
| const e = _e; | ||
| switch (e.type) { | ||
| case "pacer:AsyncBatcher": | ||
| setStore({ | ||
| asyncBatchers: updateOrAddToArray(store.asyncBatchers, e.payload), | ||
| lastUpdatedByKey: { | ||
| ...store.lastUpdatedByKey, | ||
| [e.payload.key]: Date.now() | ||
| } | ||
| }); | ||
| break; | ||
| case "pacer:AsyncDebouncer": | ||
| setStore({ | ||
| asyncDebouncers: updateOrAddToArray(store.asyncDebouncers, e.payload), | ||
| lastUpdatedByKey: { | ||
| ...store.lastUpdatedByKey, | ||
| [e.payload.key]: Date.now() | ||
| } | ||
| }); | ||
| break; | ||
| case "pacer:AsyncQueuer": | ||
| setStore({ | ||
| asyncQueuers: updateOrAddToArray(store.asyncQueuers, e.payload), | ||
| lastUpdatedByKey: { | ||
| ...store.lastUpdatedByKey, | ||
| [e.payload.key]: Date.now() | ||
| } | ||
| }); | ||
| break; | ||
| case "pacer:AsyncRateLimiter": | ||
| setStore({ | ||
| asyncRateLimiters: updateOrAddToArray(store.asyncRateLimiters, e.payload), | ||
| lastUpdatedByKey: { | ||
| ...store.lastUpdatedByKey, | ||
| [e.payload.key]: Date.now() | ||
| } | ||
| }); | ||
| break; | ||
| case "pacer:AsyncThrottler": | ||
| setStore({ | ||
| asyncThrottlers: updateOrAddToArray(store.asyncThrottlers, e.payload), | ||
| lastUpdatedByKey: { | ||
| ...store.lastUpdatedByKey, | ||
| [e.payload.key]: Date.now() | ||
| } | ||
| }); | ||
| break; | ||
| case "pacer:Batcher": | ||
| setStore({ | ||
| batchers: updateOrAddToArray(store.batchers, e.payload), | ||
| lastUpdatedByKey: { | ||
| ...store.lastUpdatedByKey, | ||
| [e.payload.key]: Date.now() | ||
| } | ||
| }); | ||
| break; | ||
| case "pacer:Debouncer": | ||
| setStore({ | ||
| debouncers: updateOrAddToArray(store.debouncers, e.payload), | ||
| lastUpdatedByKey: { | ||
| ...store.lastUpdatedByKey, | ||
| [e.payload.key]: Date.now() | ||
| } | ||
| }); | ||
| break; | ||
| case "pacer:Queuer": | ||
| setStore({ | ||
| queuers: updateOrAddToArray(store.queuers, e.payload), | ||
| lastUpdatedByKey: { | ||
| ...store.lastUpdatedByKey, | ||
| [e.payload.key]: Date.now() | ||
| } | ||
| }); | ||
| break; | ||
| case "pacer:RateLimiter": | ||
| setStore({ | ||
| rateLimiters: updateOrAddToArray(store.rateLimiters, e.payload), | ||
| lastUpdatedByKey: { | ||
| ...store.lastUpdatedByKey, | ||
| [e.payload.key]: Date.now() | ||
| } | ||
| }); | ||
| break; | ||
| case "pacer:Throttler": | ||
| setStore({ | ||
| throttlers: updateOrAddToArray(store.throttlers, e.payload), | ||
| lastUpdatedByKey: { | ||
| ...store.lastUpdatedByKey, | ||
| [e.payload.key]: Date.now() | ||
| } | ||
| }); | ||
| break; | ||
| } | ||
| })); | ||
| }); | ||
| return createComponent(PacerDevtoolsContext.Provider, { | ||
| value: [store, setStore], | ||
| get children() { | ||
| return props.children; | ||
| } | ||
| }); | ||
| } | ||
| const usePacerDevtoolsContext = () => { | ||
| return useContext(PacerDevtoolsContext); | ||
| }; | ||
| const usePacerDevtoolsState = () => { | ||
| const [state] = usePacerDevtoolsContext(); | ||
| return state; | ||
| }; | ||
| //#endregion | ||
| //#region src/styles/tokens.ts | ||
| const tokens = { | ||
| colors: { | ||
| inherit: "inherit", | ||
| current: "currentColor", | ||
| transparent: "transparent", | ||
| black: "#000000", | ||
| white: "#ffffff", | ||
| neutral: { | ||
| 50: "#f9fafb", | ||
| 100: "#f2f4f7", | ||
| 200: "#eaecf0", | ||
| 300: "#d0d5dd", | ||
| 400: "#98a2b3", | ||
| 500: "#667085", | ||
| 600: "#475467", | ||
| 700: "#344054", | ||
| 800: "#1d2939", | ||
| 900: "#101828" | ||
| }, | ||
| darkGray: { | ||
| 50: "#525c7a", | ||
| 100: "#49536e", | ||
| 200: "#414962", | ||
| 300: "#394056", | ||
| 400: "#313749", | ||
| 500: "#292e3d", | ||
| 600: "#212530", | ||
| 700: "#191c24", | ||
| 800: "#111318", | ||
| 900: "#0b0d10" | ||
| }, | ||
| gray: { | ||
| 50: "#f9fafb", | ||
| 100: "#f2f4f7", | ||
| 200: "#eaecf0", | ||
| 300: "#d0d5dd", | ||
| 400: "#98a2b3", | ||
| 500: "#667085", | ||
| 600: "#475467", | ||
| 700: "#344054", | ||
| 800: "#1d2939", | ||
| 900: "#101828" | ||
| }, | ||
| blue: { | ||
| 25: "#F5FAFF", | ||
| 50: "#EFF8FF", | ||
| 100: "#D1E9FF", | ||
| 200: "#B2DDFF", | ||
| 300: "#84CAFF", | ||
| 400: "#53B1FD", | ||
| 500: "#2E90FA", | ||
| 600: "#1570EF", | ||
| 700: "#175CD3", | ||
| 800: "#1849A9", | ||
| 900: "#194185" | ||
| }, | ||
| green: { | ||
| 25: "#F6FEF9", | ||
| 50: "#ECFDF3", | ||
| 100: "#D1FADF", | ||
| 200: "#A6F4C5", | ||
| 300: "#6CE9A6", | ||
| 400: "#32D583", | ||
| 500: "#12B76A", | ||
| 600: "#039855", | ||
| 700: "#027A48", | ||
| 800: "#05603A", | ||
| 900: "#054F31" | ||
| }, | ||
| red: { | ||
| 50: "#fef2f2", | ||
| 100: "#fee2e2", | ||
| 200: "#fecaca", | ||
| 300: "#fca5a5", | ||
| 400: "#f87171", | ||
| 500: "#ef4444", | ||
| 600: "#dc2626", | ||
| 700: "#b91c1c", | ||
| 800: "#991b1b", | ||
| 900: "#7f1d1d", | ||
| 950: "#450a0a" | ||
| }, | ||
| yellow: { | ||
| 25: "#FFFCF5", | ||
| 50: "#FFFAEB", | ||
| 100: "#FEF0C7", | ||
| 200: "#FEDF89", | ||
| 300: "#FEC84B", | ||
| 400: "#FDB022", | ||
| 500: "#F79009", | ||
| 600: "#DC6803", | ||
| 700: "#B54708", | ||
| 800: "#93370D", | ||
| 900: "#7A2E0E" | ||
| }, | ||
| purple: { | ||
| 25: "#FAFAFF", | ||
| 50: "#F4F3FF", | ||
| 100: "#EBE9FE", | ||
| 200: "#D9D6FE", | ||
| 300: "#BDB4FE", | ||
| 400: "#9B8AFB", | ||
| 500: "#7A5AF8", | ||
| 600: "#6938EF", | ||
| 700: "#5925DC", | ||
| 800: "#4A1FB8", | ||
| 900: "#3E1C96" | ||
| }, | ||
| teal: { | ||
| 25: "#F6FEFC", | ||
| 50: "#F0FDF9", | ||
| 100: "#CCFBEF", | ||
| 200: "#99F6E0", | ||
| 300: "#5FE9D0", | ||
| 400: "#2ED3B7", | ||
| 500: "#15B79E", | ||
| 600: "#0E9384", | ||
| 700: "#107569", | ||
| 800: "#125D56", | ||
| 900: "#134E48" | ||
| }, | ||
| pink: { | ||
| 25: "#fdf2f8", | ||
| 50: "#fce7f3", | ||
| 100: "#fbcfe8", | ||
| 200: "#f9a8d4", | ||
| 300: "#f472b6", | ||
| 400: "#ec4899", | ||
| 500: "#db2777", | ||
| 600: "#be185d", | ||
| 700: "#9d174d", | ||
| 800: "#831843", | ||
| 900: "#500724" | ||
| }, | ||
| cyan: { | ||
| 25: "#ecfeff", | ||
| 50: "#cffafe", | ||
| 100: "#a5f3fc", | ||
| 200: "#67e8f9", | ||
| 300: "#22d3ee", | ||
| 400: "#06b6d4", | ||
| 500: "#0891b2", | ||
| 600: "#0e7490", | ||
| 700: "#155e75", | ||
| 800: "#164e63", | ||
| 900: "#083344" | ||
| } | ||
| }, | ||
| alpha: { | ||
| 100: "ff", | ||
| 90: "e5", | ||
| 80: "cc", | ||
| 70: "b3", | ||
| 60: "99", | ||
| 50: "80", | ||
| 40: "66", | ||
| 30: "4d", | ||
| 20: "33", | ||
| 10: "1a", | ||
| 0: "00" | ||
| }, | ||
| font: { | ||
| size: { | ||
| "2xs": "calc(var(--tsrd-font-size) * 0.625)", | ||
| xs: "calc(var(--tsrd-font-size) * 0.75)", | ||
| sm: "calc(var(--tsrd-font-size) * 0.875)", | ||
| md: "var(--tsrd-font-size)", | ||
| lg: "calc(var(--tsrd-font-size) * 1.125)", | ||
| xl: "calc(var(--tsrd-font-size) * 1.25)", | ||
| "2xl": "calc(var(--tsrd-font-size) * 1.5)", | ||
| "3xl": "calc(var(--tsrd-font-size) * 1.875)", | ||
| "4xl": "calc(var(--tsrd-font-size) * 2.25)", | ||
| "5xl": "calc(var(--tsrd-font-size) * 3)", | ||
| "6xl": "calc(var(--tsrd-font-size) * 3.75)", | ||
| "7xl": "calc(var(--tsrd-font-size) * 4.5)", | ||
| "8xl": "calc(var(--tsrd-font-size) * 6)", | ||
| "9xl": "calc(var(--tsrd-font-size) * 8)" | ||
| }, | ||
| lineHeight: { | ||
| "3xs": "calc(var(--tsrd-font-size) * 0.75)", | ||
| "2xs": "calc(var(--tsrd-font-size) * 0.875)", | ||
| xs: "calc(var(--tsrd-font-size) * 1)", | ||
| sm: "calc(var(--tsrd-font-size) * 1.25)", | ||
| md: "calc(var(--tsrd-font-size) * 1.5)", | ||
| lg: "calc(var(--tsrd-font-size) * 1.75)", | ||
| xl: "calc(var(--tsrd-font-size) * 2)", | ||
| "2xl": "calc(var(--tsrd-font-size) * 2.25)", | ||
| "3xl": "calc(var(--tsrd-font-size) * 2.5)", | ||
| "4xl": "calc(var(--tsrd-font-size) * 2.75)", | ||
| "5xl": "calc(var(--tsrd-font-size) * 3)", | ||
| "6xl": "calc(var(--tsrd-font-size) * 3.25)", | ||
| "7xl": "calc(var(--tsrd-font-size) * 3.5)", | ||
| "8xl": "calc(var(--tsrd-font-size) * 3.75)", | ||
| "9xl": "calc(var(--tsrd-font-size) * 4)" | ||
| }, | ||
| weight: { | ||
| thin: "100", | ||
| extralight: "200", | ||
| light: "300", | ||
| normal: "400", | ||
| medium: "500", | ||
| semibold: "600", | ||
| bold: "700", | ||
| extrabold: "800", | ||
| black: "900" | ||
| }, | ||
| fontFamily: { | ||
| sans: "ui-sans-serif, Inter, system-ui, sans-serif, sans-serif", | ||
| mono: `ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace` | ||
| } | ||
| }, | ||
| breakpoints: { | ||
| xs: "320px", | ||
| sm: "640px", | ||
| md: "768px", | ||
| lg: "1024px", | ||
| xl: "1280px", | ||
| "2xl": "1536px" | ||
| }, | ||
| border: { radius: { | ||
| none: "0px", | ||
| xs: "calc(var(--tsrd-font-size) * 0.125)", | ||
| sm: "calc(var(--tsrd-font-size) * 0.25)", | ||
| md: "calc(var(--tsrd-font-size) * 0.375)", | ||
| lg: "calc(var(--tsrd-font-size) * 0.5)", | ||
| xl: "calc(var(--tsrd-font-size) * 0.75)", | ||
| "2xl": "calc(var(--tsrd-font-size) * 1)", | ||
| "3xl": "calc(var(--tsrd-font-size) * 1.5)", | ||
| full: "9999px" | ||
| } }, | ||
| size: { | ||
| 0: "0px", | ||
| .25: "calc(var(--tsrd-font-size) * 0.0625)", | ||
| .5: "calc(var(--tsrd-font-size) * 0.125)", | ||
| 1: "calc(var(--tsrd-font-size) * 0.25)", | ||
| 1.5: "calc(var(--tsrd-font-size) * 0.375)", | ||
| 2: "calc(var(--tsrd-font-size) * 0.5)", | ||
| 2.5: "calc(var(--tsrd-font-size) * 0.625)", | ||
| 3: "calc(var(--tsrd-font-size) * 0.75)", | ||
| 3.5: "calc(var(--tsrd-font-size) * 0.875)", | ||
| 4: "calc(var(--tsrd-font-size) * 1)", | ||
| 4.5: "calc(var(--tsrd-font-size) * 1.125)", | ||
| 5: "calc(var(--tsrd-font-size) * 1.25)", | ||
| 5.5: "calc(var(--tsrd-font-size) * 1.375)", | ||
| 6: "calc(var(--tsrd-font-size) * 1.5)", | ||
| 6.5: "calc(var(--tsrd-font-size) * 1.625)", | ||
| 7: "calc(var(--tsrd-font-size) * 1.75)", | ||
| 8: "calc(var(--tsrd-font-size) * 2)", | ||
| 9: "calc(var(--tsrd-font-size) * 2.25)", | ||
| 10: "calc(var(--tsrd-font-size) * 2.5)", | ||
| 11: "calc(var(--tsrd-font-size) * 2.75)", | ||
| 12: "calc(var(--tsrd-font-size) * 3)", | ||
| 14: "calc(var(--tsrd-font-size) * 3.5)", | ||
| 16: "calc(var(--tsrd-font-size) * 4)", | ||
| 20: "calc(var(--tsrd-font-size) * 5)", | ||
| 24: "calc(var(--tsrd-font-size) * 6)", | ||
| 28: "calc(var(--tsrd-font-size) * 7)", | ||
| 32: "calc(var(--tsrd-font-size) * 8)", | ||
| 36: "calc(var(--tsrd-font-size) * 9)", | ||
| 40: "calc(var(--tsrd-font-size) * 10)", | ||
| 44: "calc(var(--tsrd-font-size) * 11)", | ||
| 48: "calc(var(--tsrd-font-size) * 12)", | ||
| 52: "calc(var(--tsrd-font-size) * 13)", | ||
| 56: "calc(var(--tsrd-font-size) * 14)", | ||
| 60: "calc(var(--tsrd-font-size) * 15)", | ||
| 64: "calc(var(--tsrd-font-size) * 16)", | ||
| 72: "calc(var(--tsrd-font-size) * 18)", | ||
| 80: "calc(var(--tsrd-font-size) * 20)", | ||
| 96: "calc(var(--tsrd-font-size) * 24)" | ||
| }, | ||
| shadow: { | ||
| xs: (_ = "rgb(0 0 0 / 0.1)") => `0 1px 2px 0 rgb(0 0 0 / 0.05)`, | ||
| sm: (color = "rgb(0 0 0 / 0.1)") => `0 1px 3px 0 ${color}, 0 1px 2px -1px ${color}`, | ||
| md: (color = "rgb(0 0 0 / 0.1)") => `0 4px 6px -1px ${color}, 0 2px 4px -2px ${color}`, | ||
| lg: (color = "rgb(0 0 0 / 0.1)") => `0 10px 15px -3px ${color}, 0 4px 6px -4px ${color}`, | ||
| xl: (color = "rgb(0 0 0 / 0.1)") => `0 20px 25px -5px ${color}, 0 8px 10px -6px ${color}`, | ||
| "2xl": (color = "rgb(0 0 0 / 0.25)") => `0 25px 50px -12px ${color}`, | ||
| inner: (color = "rgb(0 0 0 / 0.05)") => `inset 0 2px 4px 0 ${color}`, | ||
| none: () => `none` | ||
| }, | ||
| zIndices: { | ||
| hide: -1, | ||
| auto: "auto", | ||
| base: 0, | ||
| docked: 10, | ||
| dropdown: 1e3, | ||
| sticky: 1100, | ||
| banner: 1200, | ||
| overlay: 1300, | ||
| modal: 1400, | ||
| popover: 1500, | ||
| skipLink: 1600, | ||
| toast: 1700, | ||
| tooltip: 1800 | ||
| } | ||
| }; | ||
| //#endregion | ||
| //#region src/styles/use-styles.ts | ||
| const stylesFactory = (theme) => { | ||
| const { colors, font, size, alpha, border } = tokens; | ||
| const { fontFamily, size: fontSize } = font; | ||
| const css = goober.css; | ||
| const t = (light, dark) => theme === "light" ? light : dark; | ||
| return { | ||
| mainContainer: css` | ||
| display: flex; | ||
| flex: 1; | ||
| min-height: 0; | ||
| overflow: hidden; | ||
| padding: ${size[2]}; | ||
| padding-top: 0; | ||
| margin-top: ${size[2]}; | ||
| `, | ||
| dragHandle: css` | ||
| width: 8px; | ||
| background: ${t(colors.gray[300], colors.darkGray[600])}; | ||
| cursor: col-resize; | ||
| position: relative; | ||
| transition: all 0.2s ease; | ||
| user-select: none; | ||
| pointer-events: all; | ||
| margin: 0 ${size[1]}; | ||
| border-radius: 2px; | ||
| &:hover { | ||
| background: ${t(colors.blue[600], colors.blue[500])}; | ||
| margin: 0 ${size[1]}; | ||
| } | ||
| &.dragging { | ||
| background: ${t(colors.blue[700], colors.blue[600])}; | ||
| margin: 0 ${size[1]}; | ||
| } | ||
| &::after { | ||
| content: ''; | ||
| position: absolute; | ||
| top: 50%; | ||
| left: 50%; | ||
| transform: translate(-50%, -50%); | ||
| width: 2px; | ||
| height: 20px; | ||
| background: ${t(colors.gray[400], colors.darkGray[400])}; | ||
| border-radius: 1px; | ||
| pointer-events: none; | ||
| } | ||
| &:hover::after, | ||
| &.dragging::after { | ||
| background: ${t(colors.blue[500], colors.blue[300])}; | ||
| } | ||
| `, | ||
| leftPanel: css` | ||
| background: ${t(colors.gray[100], colors.darkGray[800])}; | ||
| border-radius: ${border.radius.lg}; | ||
| border: 1px solid ${t(colors.gray[200], colors.darkGray[700])}; | ||
| display: flex; | ||
| flex-direction: column; | ||
| overflow: hidden; | ||
| min-height: 0; | ||
| flex-shrink: 0; | ||
| `, | ||
| rightPanel: css` | ||
| background: ${t(colors.gray[100], colors.darkGray[800])}; | ||
| border-radius: ${border.radius.lg}; | ||
| border: 1px solid ${t(colors.gray[200], colors.darkGray[700])}; | ||
| display: flex; | ||
| flex-direction: column; | ||
| overflow: hidden; | ||
| min-height: 0; | ||
| flex: 1; | ||
| `, | ||
| panelHeader: css` | ||
| font-size: ${fontSize.md}; | ||
| font-weight: ${font.weight.bold}; | ||
| color: ${t(colors.blue[700], colors.blue[400])}; | ||
| padding: ${size[2]}; | ||
| border-bottom: 1px solid ${t(colors.gray[200], colors.darkGray[700])}; | ||
| background: ${t(colors.gray[100], colors.darkGray[800])}; | ||
| flex-shrink: 0; | ||
| `, | ||
| utilList: css` | ||
| flex: 1; | ||
| overflow-y: auto; | ||
| padding: ${size[1]}; | ||
| min-height: 0; | ||
| `, | ||
| utilGroup: css` | ||
| margin-bottom: ${size[2]}; | ||
| `, | ||
| utilGroupHeader: css` | ||
| font-size: ${fontSize.xs}; | ||
| font-weight: ${font.weight.semibold}; | ||
| color: ${t(colors.gray[600], colors.gray[400])}; | ||
| text-transform: uppercase; | ||
| letter-spacing: 0.05em; | ||
| margin-bottom: ${size[1]}; | ||
| padding: ${size[1]} ${size[2]}; | ||
| background: ${t(colors.gray[200], colors.darkGray[700])}; | ||
| border-radius: ${border.radius.md}; | ||
| `, | ||
| utilRow: css` | ||
| display: flex; | ||
| justify-content: space-between; | ||
| align-items: center; | ||
| padding: ${size[2]}; | ||
| margin-bottom: ${size[1]}; | ||
| background: ${t(colors.gray[200], colors.darkGray[700])}; | ||
| border-radius: ${border.radius.md}; | ||
| cursor: pointer; | ||
| transition: all 0.2s ease; | ||
| border: 1px solid transparent; | ||
| &:hover { | ||
| background: ${t(colors.gray[300], colors.darkGray[600])}; | ||
| border-color: ${t(colors.gray[400], colors.darkGray[500])}; | ||
| } | ||
| `, | ||
| utilRowSelected: css` | ||
| background: ${t(colors.blue[100], colors.blue[900] + alpha[20])}; | ||
| border-color: ${t(colors.blue[600], colors.blue[500])}; | ||
| box-shadow: 0 0 0 1px | ||
| ${t(colors.blue[600] + alpha[30], colors.blue[500] + alpha[30])}; | ||
| `, | ||
| utilKey: css` | ||
| font-family: ${fontFamily.mono}; | ||
| font-size: ${fontSize.xs}; | ||
| color: ${t(colors.gray[900], colors.gray[100])}; | ||
| flex: 1; | ||
| overflow: hidden; | ||
| text-overflow: ellipsis; | ||
| white-space: nowrap; | ||
| `, | ||
| utilStatus: css` | ||
| font-size: ${fontSize.xs}; | ||
| color: ${t(colors.gray[600], colors.gray[400])}; | ||
| text-transform: uppercase; | ||
| letter-spacing: 0.05em; | ||
| padding: ${size[1]} ${size[1]}; | ||
| background: ${t(colors.gray[300], colors.darkGray[600])}; | ||
| border-radius: ${border.radius.sm}; | ||
| margin-left: ${size[1]}; | ||
| `, | ||
| stateDetails: css` | ||
| flex: 1; | ||
| overflow-y: auto; | ||
| padding: ${size[2]}; | ||
| min-height: 0; | ||
| `, | ||
| stateHeader: css` | ||
| margin-bottom: ${size[2]}; | ||
| padding-bottom: ${size[2]}; | ||
| border-bottom: 1px solid ${t(colors.gray[200], colors.darkGray[700])}; | ||
| `, | ||
| stateTitle: css` | ||
| font-size: ${fontSize.md}; | ||
| font-weight: ${font.weight.bold}; | ||
| color: ${t(colors.blue[700], colors.blue[400])}; | ||
| margin-bottom: ${size[1]}; | ||
| `, | ||
| stateKey: css` | ||
| font-family: ${fontFamily.mono}; | ||
| font-size: ${fontSize.xs}; | ||
| color: ${t(colors.gray[600], colors.gray[400])}; | ||
| word-break: break-all; | ||
| `, | ||
| stateContent: css` | ||
| background: ${t(colors.gray[100], colors.darkGray[700])}; | ||
| border-radius: ${border.radius.md}; | ||
| padding: ${size[2]}; | ||
| border: 1px solid ${t(colors.gray[300], colors.darkGray[600])}; | ||
| `, | ||
| detailsGrid: css` | ||
| display: grid; | ||
| grid-template-columns: 1fr; | ||
| gap: ${size[2]}; | ||
| align-items: start; | ||
| `, | ||
| detailSection: css` | ||
| background: ${t(colors.white, colors.darkGray[700])}; | ||
| border: 1px solid ${t(colors.gray[300], colors.darkGray[600])}; | ||
| border-radius: ${border.radius.md}; | ||
| padding: ${size[2]}; | ||
| `, | ||
| detailSectionHeader: css` | ||
| font-size: ${fontSize.sm}; | ||
| font-weight: ${font.weight.bold}; | ||
| color: ${t(colors.gray[800], colors.gray[200])}; | ||
| margin-bottom: ${size[1]}; | ||
| text-transform: uppercase; | ||
| letter-spacing: 0.04em; | ||
| `, | ||
| actionsRow: css` | ||
| display: flex; | ||
| flex-wrap: wrap; | ||
| gap: ${size[2]}; | ||
| `, | ||
| actionButton: css` | ||
| display: inline-flex; | ||
| align-items: center; | ||
| gap: ${size[1]}; | ||
| padding: ${size[1]} ${size[2]}; | ||
| border-radius: ${border.radius.md}; | ||
| border: 1px solid ${t(colors.gray[300], colors.darkGray[500])}; | ||
| background: ${t(colors.gray[200], colors.darkGray[600])}; | ||
| color: ${t(colors.gray[900], colors.gray[100])}; | ||
| font-size: ${fontSize.xs}; | ||
| cursor: pointer; | ||
| user-select: none; | ||
| transition: | ||
| background 0.15s, | ||
| border-color 0.15s; | ||
| &:hover { | ||
| background: ${t(colors.gray[300], colors.darkGray[500])}; | ||
| border-color: ${t(colors.gray[400], colors.darkGray[400])}; | ||
| } | ||
| &:disabled { | ||
| opacity: 0.5; | ||
| cursor: not-allowed; | ||
| &:hover { | ||
| background: ${t(colors.gray[200], colors.darkGray[600])}; | ||
| border-color: ${t(colors.gray[300], colors.darkGray[500])}; | ||
| } | ||
| } | ||
| `, | ||
| actionDotBlue: css` | ||
| width: 6px; | ||
| height: 6px; | ||
| border-radius: 9999px; | ||
| background: ${colors.blue[400]}; | ||
| `, | ||
| actionDotGreen: css` | ||
| width: 6px; | ||
| height: 6px; | ||
| border-radius: 9999px; | ||
| background: ${colors.green[400]}; | ||
| `, | ||
| actionDotRed: css` | ||
| width: 6px; | ||
| height: 6px; | ||
| border-radius: 9999px; | ||
| background: ${colors.red[400]}; | ||
| `, | ||
| actionDotYellow: css` | ||
| width: 6px; | ||
| height: 6px; | ||
| border-radius: 9999px; | ||
| background: ${colors.yellow[400]}; | ||
| `, | ||
| actionDotOrange: css` | ||
| width: 6px; | ||
| height: 6px; | ||
| border-radius: 9999px; | ||
| background: ${colors.pink[400]}; | ||
| `, | ||
| actionDotPurple: css` | ||
| width: 6px; | ||
| height: 6px; | ||
| border-radius: 9999px; | ||
| background: ${colors.purple[400]}; | ||
| `, | ||
| infoGrid: css` | ||
| display: grid; | ||
| grid-template-columns: auto 1fr; | ||
| gap: ${size[1]}; | ||
| row-gap: ${size[1]}; | ||
| align-items: center; | ||
| `, | ||
| infoLabel: css` | ||
| color: ${t(colors.gray[600], colors.gray[400])}; | ||
| font-size: ${fontSize.xs}; | ||
| text-transform: uppercase; | ||
| letter-spacing: 0.05em; | ||
| `, | ||
| infoValueMono: css` | ||
| font-family: ${fontFamily.mono}; | ||
| font-size: ${fontSize.xs}; | ||
| color: ${t(colors.gray[900], colors.gray[100])}; | ||
| word-break: break-all; | ||
| `, | ||
| noSelection: css` | ||
| flex: 1; | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: center; | ||
| color: ${t(colors.gray[500], colors.gray[500])}; | ||
| font-style: italic; | ||
| text-align: center; | ||
| padding: ${size[4]}; | ||
| `, | ||
| sectionContainer: css` | ||
| display: flex; | ||
| flex-wrap: wrap; | ||
| gap: ${size[4]}; | ||
| `, | ||
| section: css` | ||
| background: ${t(colors.gray[100], colors.darkGray[800])}; | ||
| border-radius: ${border.radius.lg}; | ||
| box-shadow: ${tokens.shadow.md(t(colors.gray[400] + alpha[80], colors.black + alpha[80]))}; | ||
| padding: ${size[4]}; | ||
| margin-bottom: ${size[4]}; | ||
| border: 1px solid ${t(colors.gray[200], colors.darkGray[700])}; | ||
| min-width: 0; | ||
| max-width: 33%; | ||
| max-height: fit-content; | ||
| `, | ||
| sectionHeader: css` | ||
| font-size: ${fontSize.lg}; | ||
| font-weight: ${font.weight.bold}; | ||
| margin-bottom: ${size[2]}; | ||
| color: ${t(colors.blue[600], colors.blue[400])}; | ||
| letter-spacing: 0.01em; | ||
| display: flex; | ||
| align-items: center; | ||
| gap: ${size[2]}; | ||
| `, | ||
| sectionEmpty: css` | ||
| color: ${t(colors.gray[500], colors.gray[500])}; | ||
| font-size: ${fontSize.sm}; | ||
| font-style: italic; | ||
| margin: ${size[2]} 0; | ||
| `, | ||
| instanceList: css` | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: ${size[2]}; | ||
| background: ${t(colors.gray[200], colors.darkGray[700])}; | ||
| border: 1px solid ${t(colors.gray[300], colors.darkGray[600])}; | ||
| `, | ||
| instanceCard: css` | ||
| background: ${t(colors.gray[200], colors.darkGray[700])}; | ||
| border-radius: ${border.radius.md}; | ||
| padding: ${size[3]}; | ||
| border: 1px solid ${t(colors.gray[300], colors.darkGray[600])}; | ||
| font-size: ${fontSize.sm}; | ||
| color: ${t(colors.gray[900], colors.gray[100])}; | ||
| font-family: ${fontFamily.mono}; | ||
| overflow-x: auto; | ||
| transition: | ||
| box-shadow 0.3s, | ||
| background 0.3s; | ||
| ` | ||
| }; | ||
| }; | ||
| function useStyles() { | ||
| const { theme } = useTheme(); | ||
| const [styles, setStyles] = createSignal(stylesFactory(theme())); | ||
| createEffect(() => { | ||
| setStyles(stylesFactory(theme())); | ||
| }); | ||
| return styles; | ||
| } | ||
| //#endregion | ||
| //#region src/components/util-groups.ts | ||
| const UTIL_GROUPS = [ | ||
| { | ||
| key: "debouncers", | ||
| label: "Debouncers", | ||
| displayName: "Debouncer" | ||
| }, | ||
| { | ||
| key: "throttlers", | ||
| label: "Throttlers", | ||
| displayName: "Throttler" | ||
| }, | ||
| { | ||
| key: "batchers", | ||
| label: "Batchers", | ||
| displayName: "Batcher" | ||
| }, | ||
| { | ||
| key: "queuers", | ||
| label: "Queuers", | ||
| displayName: "Queuer" | ||
| }, | ||
| { | ||
| key: "rateLimiters", | ||
| label: "Rate Limiters", | ||
| displayName: "Rate Limiter" | ||
| }, | ||
| { | ||
| key: "asyncDebouncers", | ||
| label: "Async Debouncers", | ||
| displayName: "Async Debouncer" | ||
| }, | ||
| { | ||
| key: "asyncThrottlers", | ||
| label: "Async Throttlers", | ||
| displayName: "Async Throttler" | ||
| }, | ||
| { | ||
| key: "asyncBatchers", | ||
| label: "Async Batchers", | ||
| displayName: "Async Batcher" | ||
| }, | ||
| { | ||
| key: "asyncQueuers", | ||
| label: "Async Queuers", | ||
| displayName: "Async Queuer" | ||
| }, | ||
| { | ||
| key: "asyncRateLimiters", | ||
| label: "Async Rate Limiters", | ||
| displayName: "Async Rate Limiter" | ||
| } | ||
| ]; | ||
| //#endregion | ||
| //#region src/components/UtilList.tsx | ||
| var _tmpl$$4 = /* @__PURE__ */ template(`<div>`), _tmpl$2$2 = /* @__PURE__ */ template(`<div><div>`), _tmpl$3$2 = /* @__PURE__ */ template(`<div><div></div><div>`); | ||
| function UtilList(props) { | ||
| const styles = useStyles(); | ||
| return (() => { | ||
| var _el$ = _tmpl$$4(); | ||
| insert(_el$, createComponent(For, { | ||
| each: UTIL_GROUPS, | ||
| children: (group) => memo(() => memo(() => props.utilState()[group.key].length > 0)() && (() => { | ||
| var _el$2 = _tmpl$2$2(), _el$3 = _el$2.firstChild; | ||
| insert(_el$3, () => group.label); | ||
| insert(_el$2, createComponent(For, { | ||
| get each() { | ||
| return props.utilState()[group.key]; | ||
| }, | ||
| children: (instance) => { | ||
| const status = (() => { | ||
| try { | ||
| const statusAccessor = useStore(instance.store, (s) => s.status); | ||
| return () => statusAccessor() ?? "unknown"; | ||
| } catch { | ||
| return () => "unknown"; | ||
| } | ||
| })(); | ||
| return (() => { | ||
| var _el$4 = _tmpl$3$2(), _el$5 = _el$4.firstChild, _el$6 = _el$5.nextSibling; | ||
| _el$4.$$click = () => props.setSelectedKey(instance.key ?? null); | ||
| insert(_el$5, () => instance.key); | ||
| insert(_el$6, status); | ||
| effect((_p$) => { | ||
| var _v$3 = clsx(styles().utilRow, props.selectedKey() === instance.key && styles().utilRowSelected), _v$4 = styles().utilKey, _v$5 = styles().utilStatus; | ||
| _v$3 !== _p$.e && className(_el$4, _p$.e = _v$3); | ||
| _v$4 !== _p$.t && className(_el$5, _p$.t = _v$4); | ||
| _v$5 !== _p$.a && className(_el$6, _p$.a = _v$5); | ||
| return _p$; | ||
| }, { | ||
| e: void 0, | ||
| t: void 0, | ||
| a: void 0 | ||
| }); | ||
| return _el$4; | ||
| })(); | ||
| } | ||
| }), null); | ||
| effect((_p$) => { | ||
| var _v$ = styles().utilGroup, _v$2 = styles().utilGroupHeader; | ||
| _v$ !== _p$.e && className(_el$2, _p$.e = _v$); | ||
| _v$2 !== _p$.t && className(_el$3, _p$.t = _v$2); | ||
| return _p$; | ||
| }, { | ||
| e: void 0, | ||
| t: void 0 | ||
| }); | ||
| return _el$2; | ||
| })()) | ||
| })); | ||
| effect(() => className(_el$, styles().utilList)); | ||
| return _el$; | ||
| })(); | ||
| } | ||
| delegateEvents(["click"]); | ||
| //#endregion | ||
| //#region src/components/ActionButtons.tsx | ||
| var _tmpl$$3 = /* @__PURE__ */ template(`<div>No actions available for this util`), _tmpl$2$1 = /* @__PURE__ */ template(`<div>`), _tmpl$3$1 = /* @__PURE__ */ template(`<button><span>`), _tmpl$4 = /* @__PURE__ */ template(`<button><span></span>Flush`), _tmpl$5 = /* @__PURE__ */ template(`<button><span></span>Cancel`), _tmpl$6 = /* @__PURE__ */ template(`<button><span></span>Reset`), _tmpl$7 = /* @__PURE__ */ template(`<button><span></span>Clear`); | ||
| function ActionButtons(props) { | ||
| const styles = useStyles(); | ||
| const utilInstance = props.instance; | ||
| const utilState = utilInstance?.store?.state; | ||
| const hasPending = utilState && "isPending" in utilState; | ||
| const hasEmpty = utilState && "isEmpty" in utilState; | ||
| const isPending = hasPending ? !!utilState.isPending : false; | ||
| const isEmpty = hasEmpty ? utilState.isEmpty : void 0; | ||
| const hasFlush = typeof utilInstance.flush === "function"; | ||
| const hasCancel = typeof utilInstance.cancel === "function"; | ||
| const hasReset = typeof utilInstance.reset === "function"; | ||
| const hasClear = typeof utilInstance.clear === "function"; | ||
| const hasStart = typeof utilInstance.start === "function"; | ||
| const hasStop = typeof utilInstance.stop === "function"; | ||
| const hasStartStop = hasStart && hasStop; | ||
| const isRunning = utilState?.isRunning ?? true; | ||
| if (!hasPending && !hasFlush && !hasCancel && !hasReset && !hasClear) return (() => { | ||
| var _el$ = _tmpl$$3(); | ||
| effect(() => className(_el$, styles().sectionEmpty)); | ||
| return _el$; | ||
| })(); | ||
| const emitName = `d-${props.utilName}`; | ||
| return (() => { | ||
| var _el$2 = _tmpl$2$1(); | ||
| insert(_el$2, hasPending && (() => { | ||
| var _el$3 = _tmpl$3$1(), _el$4 = _el$3.firstChild; | ||
| _el$3.$$mousedown = () => { | ||
| const next = !isPending; | ||
| utilInstance.store.setState((prev) => ({ | ||
| ...prev, | ||
| isPending: next | ||
| })); | ||
| pacerEventClient$1.emit(emitName, utilInstance); | ||
| }; | ||
| insert(_el$3, isPending ? "Restore Pending" : "Trigger Pending", null); | ||
| effect((_p$) => { | ||
| var _v$ = styles().actionButton, _v$2 = styles().actionDotBlue; | ||
| _v$ !== _p$.e && className(_el$3, _p$.e = _v$); | ||
| _v$2 !== _p$.t && className(_el$4, _p$.t = _v$2); | ||
| return _p$; | ||
| }, { | ||
| e: void 0, | ||
| t: void 0 | ||
| }); | ||
| return _el$3; | ||
| })(), null); | ||
| insert(_el$2, hasFlush && (() => { | ||
| var _el$5 = _tmpl$4(), _el$6 = _el$5.firstChild; | ||
| _el$5.$$mousedown = () => { | ||
| utilInstance.flush(); | ||
| pacerEventClient$1.emit(emitName, utilInstance); | ||
| }; | ||
| effect((_p$) => { | ||
| var _v$3 = styles().actionButton, _v$4 = props.utilName.toLowerCase().includes("debouncer") && !isPending || props.utilName.toLowerCase().includes("throttler") && !isPending || props.utilName.toLowerCase().includes("batcher") && isEmpty || props.utilName.toLowerCase().includes("queuer") && isEmpty, _v$5 = styles().actionDotGreen; | ||
| _v$3 !== _p$.e && className(_el$5, _p$.e = _v$3); | ||
| _v$4 !== _p$.t && (_el$5.disabled = _p$.t = _v$4); | ||
| _v$5 !== _p$.a && className(_el$6, _p$.a = _v$5); | ||
| return _p$; | ||
| }, { | ||
| e: void 0, | ||
| t: void 0, | ||
| a: void 0 | ||
| }); | ||
| return _el$5; | ||
| })(), null); | ||
| insert(_el$2, hasCancel && (() => { | ||
| var _el$7 = _tmpl$5(), _el$8 = _el$7.firstChild; | ||
| _el$7.$$mousedown = () => { | ||
| utilInstance.cancel(); | ||
| pacerEventClient$1.emit(emitName, utilInstance); | ||
| }; | ||
| effect((_p$) => { | ||
| var _v$6 = styles().actionButton, _v$7 = props.utilName.toLowerCase().includes("debouncer") && !isPending || props.utilName.toLowerCase().includes("throttler") && !isPending || props.utilName.toLowerCase().includes("batcher") && isEmpty || props.utilName.toLowerCase().includes("queuer") && isEmpty, _v$8 = styles().actionDotRed; | ||
| _v$6 !== _p$.e && className(_el$7, _p$.e = _v$6); | ||
| _v$7 !== _p$.t && (_el$7.disabled = _p$.t = _v$7); | ||
| _v$8 !== _p$.a && className(_el$8, _p$.a = _v$8); | ||
| return _p$; | ||
| }, { | ||
| e: void 0, | ||
| t: void 0, | ||
| a: void 0 | ||
| }); | ||
| return _el$7; | ||
| })(), null); | ||
| insert(_el$2, hasReset && (() => { | ||
| var _el$9 = _tmpl$6(), _el$0 = _el$9.firstChild; | ||
| _el$9.$$mousedown = () => { | ||
| utilInstance.reset(); | ||
| pacerEventClient$1.emit(emitName, utilInstance); | ||
| }; | ||
| effect((_p$) => { | ||
| var _v$9 = styles().actionButton, _v$0 = styles().actionDotYellow; | ||
| _v$9 !== _p$.e && className(_el$9, _p$.e = _v$9); | ||
| _v$0 !== _p$.t && className(_el$0, _p$.t = _v$0); | ||
| return _p$; | ||
| }, { | ||
| e: void 0, | ||
| t: void 0 | ||
| }); | ||
| return _el$9; | ||
| })(), null); | ||
| insert(_el$2, hasClear && (() => { | ||
| var _el$1 = _tmpl$7(), _el$10 = _el$1.firstChild; | ||
| _el$1.$$mousedown = () => { | ||
| utilInstance.clear(); | ||
| pacerEventClient$1.emit(emitName, utilInstance); | ||
| }; | ||
| effect((_p$) => { | ||
| var _v$1 = styles().actionButton, _v$10 = props.utilName.toLowerCase().includes("batcher") && isEmpty || props.utilName.toLowerCase().includes("queuer") && isEmpty, _v$11 = styles().actionDotOrange; | ||
| _v$1 !== _p$.e && className(_el$1, _p$.e = _v$1); | ||
| _v$10 !== _p$.t && (_el$1.disabled = _p$.t = _v$10); | ||
| _v$11 !== _p$.a && className(_el$10, _p$.a = _v$11); | ||
| return _p$; | ||
| }, { | ||
| e: void 0, | ||
| t: void 0, | ||
| a: void 0 | ||
| }); | ||
| return _el$1; | ||
| })(), null); | ||
| insert(_el$2, hasStartStop && (() => { | ||
| var _el$11 = _tmpl$3$1(), _el$12 = _el$11.firstChild; | ||
| _el$11.$$mousedown = () => { | ||
| if (isRunning) utilInstance.stop(); | ||
| else utilInstance.start(); | ||
| pacerEventClient$1.emit(emitName, utilInstance); | ||
| }; | ||
| insert(_el$11, isRunning ? "Stop" : "Start", null); | ||
| effect((_p$) => { | ||
| var _v$12 = styles().actionButton, _v$13 = styles().actionDotPurple; | ||
| _v$12 !== _p$.e && className(_el$11, _p$.e = _v$12); | ||
| _v$13 !== _p$.t && className(_el$12, _p$.t = _v$13); | ||
| return _p$; | ||
| }, { | ||
| e: void 0, | ||
| t: void 0 | ||
| }); | ||
| return _el$11; | ||
| })(), null); | ||
| effect(() => className(_el$2, styles().actionsRow)); | ||
| return _el$2; | ||
| })(); | ||
| } | ||
| delegateEvents(["mousedown"]); | ||
| //#endregion | ||
| //#region src/components/StateHeader.tsx | ||
| var _tmpl$$2 = /* @__PURE__ */ template(`<div><div></div><div style=display:flex;align-items:center;gap:16px><div><div>Key</div><div></div><div>Last Updated</div><div> (<!>)</div></div><div style=margin-left:auto;font-weight:bold>% reduction`); | ||
| dayjs.extend(relativeTime); | ||
| function StateHeader(props) { | ||
| const styles = useStyles(); | ||
| const [now, setNow] = createSignal(Date.now()); | ||
| onMount(() => { | ||
| const interval = setInterval(() => { | ||
| setNow(Date.now()); | ||
| }, 1e3); | ||
| onCleanup(() => { | ||
| clearInterval(interval); | ||
| }); | ||
| }); | ||
| const entry = props.selectedInstance(); | ||
| if (!entry) return null; | ||
| const key = entry.instance.key; | ||
| const updatedAt = props.utilState().lastUpdatedByKey[key] ?? Date.now(); | ||
| const getRelativeTime = () => { | ||
| const diffMs = now() - updatedAt; | ||
| const diffSeconds = Math.floor(diffMs / 1e3); | ||
| if (diffSeconds < 60) return `${diffSeconds} second${diffSeconds !== 1 ? "s" : ""} ago`; | ||
| return dayjs(updatedAt).fromNow(); | ||
| }; | ||
| const getReductionPercentage = () => { | ||
| const state = entry.instance.store?.state || entry.instance.state; | ||
| if (!state) return 0; | ||
| const completedExecutions = entry.type.toLowerCase().includes("async") ? state.settleCount || 0 : state.executionCount || 0; | ||
| if (entry.type.toLowerCase().includes("batcher")) { | ||
| const totalItemsProcessed = state.totalItemsProcessed || 0; | ||
| if (totalItemsProcessed === 0) return 0; | ||
| return Math.round((totalItemsProcessed - completedExecutions) / totalItemsProcessed * 100); | ||
| } | ||
| let requestCount = 0; | ||
| if (state.maybeExecuteCount !== void 0) requestCount = state.maybeExecuteCount; | ||
| else if (state.addItemCount !== void 0) requestCount = state.addItemCount; | ||
| else return 0; | ||
| if (requestCount === 0) return 0; | ||
| const reduction = requestCount - completedExecutions; | ||
| return Math.round(reduction / requestCount * 100); | ||
| }; | ||
| const reductionPercentage = getReductionPercentage(); | ||
| return (() => { | ||
| var _el$ = _tmpl$$2(), _el$2 = _el$.firstChild, _el$4 = _el$2.nextSibling.firstChild, _el$5 = _el$4.firstChild, _el$6 = _el$5.nextSibling, _el$7 = _el$6.nextSibling, _el$8 = _el$7.nextSibling, _el$9 = _el$8.firstChild, _el$1 = _el$9.nextSibling; | ||
| _el$1.nextSibling; | ||
| var _el$10 = _el$4.nextSibling, _el$11 = _el$10.firstChild; | ||
| insert(_el$2, () => entry.type); | ||
| insert(_el$6, key); | ||
| insert(_el$8, () => new Date(updatedAt).toLocaleTimeString(), _el$9); | ||
| insert(_el$8, getRelativeTime, _el$1); | ||
| insert(_el$10, reductionPercentage, _el$11); | ||
| effect((_p$) => { | ||
| var _v$ = styles().stateHeader, _v$2 = styles().stateTitle, _v$3 = styles().infoGrid, _v$4 = styles().infoLabel, _v$5 = styles().infoValueMono, _v$6 = styles().infoLabel, _v$7 = styles().infoValueMono, _v$8 = styles().infoValueMono; | ||
| _v$ !== _p$.e && className(_el$, _p$.e = _v$); | ||
| _v$2 !== _p$.t && className(_el$2, _p$.t = _v$2); | ||
| _v$3 !== _p$.a && className(_el$4, _p$.a = _v$3); | ||
| _v$4 !== _p$.o && className(_el$5, _p$.o = _v$4); | ||
| _v$5 !== _p$.i && className(_el$6, _p$.i = _v$5); | ||
| _v$6 !== _p$.n && className(_el$7, _p$.n = _v$6); | ||
| _v$7 !== _p$.s && className(_el$8, _p$.s = _v$7); | ||
| _v$8 !== _p$.h && className(_el$10, _p$.h = _v$8); | ||
| return _p$; | ||
| }, { | ||
| e: void 0, | ||
| t: void 0, | ||
| a: void 0, | ||
| o: void 0, | ||
| i: void 0, | ||
| n: void 0, | ||
| s: void 0, | ||
| h: void 0 | ||
| }); | ||
| return _el$; | ||
| })(); | ||
| } | ||
| //#endregion | ||
| //#region src/components/DetailsPanel.tsx | ||
| var _tmpl$$1 = /* @__PURE__ */ template(`<div>`), _tmpl$2 = /* @__PURE__ */ template(`<div>Select a util from the left panel to view its state`), _tmpl$3 = /* @__PURE__ */ template(`<div><div><div>Actions</div></div><div><div>State</div><div></div></div><div><div>Options</div><div>`); | ||
| function DetailsPanel(props) { | ||
| const styles = useStyles(); | ||
| return (() => { | ||
| var _el$ = _tmpl$$1(); | ||
| insert(_el$, () => { | ||
| const entry = props.selectedInstance(); | ||
| if (!entry) return (() => { | ||
| var _el$2 = _tmpl$2(); | ||
| effect(() => className(_el$2, styles().noSelection)); | ||
| return _el$2; | ||
| })(); | ||
| return [createComponent(StateHeader, { | ||
| get selectedInstance() { | ||
| return props.selectedInstance; | ||
| }, | ||
| get utilState() { | ||
| return props.utilState; | ||
| } | ||
| }), (() => { | ||
| var _el$3 = _tmpl$3(), _el$4 = _el$3.firstChild, _el$5 = _el$4.firstChild, _el$6 = _el$4.nextSibling, _el$7 = _el$6.firstChild, _el$8 = _el$7.nextSibling, _el$9 = _el$6.nextSibling, _el$0 = _el$9.firstChild, _el$1 = _el$0.nextSibling; | ||
| insert(_el$4, createComponent(ActionButtons, { | ||
| get instance() { | ||
| return entry.instance; | ||
| }, | ||
| get utilName() { | ||
| return entry.type; | ||
| } | ||
| }), null); | ||
| insert(_el$8, createComponent(JsonTree, { get value() { | ||
| return entry.instance.store?.state; | ||
| } })); | ||
| insert(_el$1, createComponent(JsonTree, { get value() { | ||
| return entry.instance.options; | ||
| } })); | ||
| effect((_p$) => { | ||
| var _v$ = styles().detailsGrid, _v$2 = styles().detailSection, _v$3 = styles().detailSectionHeader, _v$4 = styles().detailSection, _v$5 = styles().detailSectionHeader, _v$6 = styles().stateContent, _v$7 = styles().detailSection, _v$8 = styles().detailSectionHeader, _v$9 = styles().stateContent; | ||
| _v$ !== _p$.e && className(_el$3, _p$.e = _v$); | ||
| _v$2 !== _p$.t && className(_el$4, _p$.t = _v$2); | ||
| _v$3 !== _p$.a && className(_el$5, _p$.a = _v$3); | ||
| _v$4 !== _p$.o && className(_el$6, _p$.o = _v$4); | ||
| _v$5 !== _p$.i && className(_el$7, _p$.i = _v$5); | ||
| _v$6 !== _p$.n && className(_el$8, _p$.n = _v$6); | ||
| _v$7 !== _p$.s && className(_el$9, _p$.s = _v$7); | ||
| _v$8 !== _p$.h && className(_el$0, _p$.h = _v$8); | ||
| _v$9 !== _p$.r && className(_el$1, _p$.r = _v$9); | ||
| return _p$; | ||
| }, { | ||
| e: void 0, | ||
| t: void 0, | ||
| a: void 0, | ||
| o: void 0, | ||
| i: void 0, | ||
| n: void 0, | ||
| s: void 0, | ||
| h: void 0, | ||
| r: void 0 | ||
| }); | ||
| return _el$3; | ||
| })()]; | ||
| }); | ||
| effect(() => className(_el$, styles().stateDetails)); | ||
| return _el$; | ||
| })(); | ||
| } | ||
| //#endregion | ||
| //#region src/components/Shell.tsx | ||
| var _tmpl$ = /* @__PURE__ */ template(`<div><div style=min-width:150px;max-width:800px></div><div></div><div style=flex:1><div>Details`); | ||
| function Shell() { | ||
| const styles = useStyles(); | ||
| const state = usePacerDevtoolsState(); | ||
| const utilState = () => state; | ||
| const [selectedKey, setSelectedKey] = createSignal(null); | ||
| const [leftPanelWidth, setLeftPanelWidth] = createSignal(300); | ||
| const [isDragging, setIsDragging] = createSignal(false); | ||
| const selectedInstance = createMemo(() => { | ||
| const key = selectedKey(); | ||
| if (!key) return null; | ||
| for (const group of UTIL_GROUPS) { | ||
| const instance = utilState()[group.key].find((inst) => inst.key === key); | ||
| if (instance) return { | ||
| instance, | ||
| type: group.displayName | ||
| }; | ||
| } | ||
| return null; | ||
| }); | ||
| let dragStartX = 0; | ||
| let dragStartWidth = 0; | ||
| const handleMouseDown = (e) => { | ||
| e.preventDefault(); | ||
| e.stopPropagation(); | ||
| setIsDragging(true); | ||
| document.body.style.cursor = "col-resize"; | ||
| document.body.style.userSelect = "none"; | ||
| dragStartX = e.clientX; | ||
| dragStartWidth = leftPanelWidth(); | ||
| }; | ||
| const handleMouseMove = (e) => { | ||
| if (!isDragging()) return; | ||
| e.preventDefault(); | ||
| const deltaX = e.clientX - dragStartX; | ||
| setLeftPanelWidth(Math.max(150, Math.min(800, dragStartWidth + deltaX))); | ||
| }; | ||
| const handleMouseUp = () => { | ||
| setIsDragging(false); | ||
| document.body.style.cursor = ""; | ||
| document.body.style.userSelect = ""; | ||
| }; | ||
| onMount(() => { | ||
| document.addEventListener("mousemove", handleMouseMove); | ||
| document.addEventListener("mouseup", handleMouseUp); | ||
| }); | ||
| onCleanup(() => { | ||
| document.removeEventListener("mousemove", handleMouseMove); | ||
| document.removeEventListener("mouseup", handleMouseUp); | ||
| }); | ||
| return createComponent(MainPanel, { get children() { | ||
| return [createComponent(Header, { get children() { | ||
| return createComponent(HeaderLogo, { | ||
| flavor: { | ||
| light: "#84cc16", | ||
| dark: "#84cc16" | ||
| }, | ||
| children: "TanStack Pacer" | ||
| }); | ||
| } }), (() => { | ||
| var _el$ = _tmpl$(), _el$2 = _el$.firstChild, _el$3 = _el$2.nextSibling, _el$4 = _el$3.nextSibling, _el$5 = _el$4.firstChild; | ||
| insert(_el$2, createComponent(UtilList, { | ||
| selectedKey, | ||
| setSelectedKey, | ||
| utilState | ||
| })); | ||
| _el$3.$$mousedown = handleMouseDown; | ||
| insert(_el$4, createComponent(DetailsPanel, { | ||
| selectedInstance, | ||
| utilState | ||
| }), null); | ||
| effect((_p$) => { | ||
| var _v$ = styles().mainContainer, _v$2 = styles().leftPanel, _v$3 = `${leftPanelWidth()}px`, _v$4 = `${styles().dragHandle} ${isDragging() ? "dragging" : ""}`, _v$5 = styles().rightPanel, _v$6 = styles().panelHeader; | ||
| _v$ !== _p$.e && className(_el$, _p$.e = _v$); | ||
| _v$2 !== _p$.t && className(_el$2, _p$.t = _v$2); | ||
| _v$3 !== _p$.a && setStyleProperty(_el$2, "width", _p$.a = _v$3); | ||
| _v$4 !== _p$.o && className(_el$3, _p$.o = _v$4); | ||
| _v$5 !== _p$.i && className(_el$4, _p$.i = _v$5); | ||
| _v$6 !== _p$.n && className(_el$5, _p$.n = _v$6); | ||
| return _p$; | ||
| }, { | ||
| e: void 0, | ||
| t: void 0, | ||
| a: void 0, | ||
| o: void 0, | ||
| i: void 0, | ||
| n: void 0 | ||
| }); | ||
| return _el$; | ||
| })()]; | ||
| } }); | ||
| } | ||
| delegateEvents(["mousedown"]); | ||
| //#endregion | ||
| //#region src/PacerDevtools.tsx | ||
| function PacerDevtools(props) { | ||
| return createComponent(ThemeContextProvider, { | ||
| get theme() { | ||
| return props.theme; | ||
| }, | ||
| get children() { | ||
| return createComponent(PacerContextProvider, { get children() { | ||
| return createComponent(Shell, {}); | ||
| } }); | ||
| } | ||
| }); | ||
| } | ||
| //#endregion | ||
| export { PacerDevtools as default }; |
| import { createComponent, escape, ssr, ssrAttribute, ssrStyleProperty } from "solid-js/web"; | ||
| import { Header, HeaderLogo, JsonTree, MainPanel, ThemeContextProvider, useTheme } from "@tanstack/devtools-ui"; | ||
| import { createStore } from "solid-js/store"; | ||
| import { For, createContext, createEffect, createMemo, createSignal, onCleanup, onMount, useContext } from "solid-js"; | ||
| import { pacerEventClient } from "@tanstack/pacer/event-client"; | ||
| import * as goober from "goober"; | ||
| import { useStore } from "@tanstack/solid-store"; | ||
| import clsx from "clsx"; | ||
| import dayjs from "dayjs"; | ||
| import relativeTime from "dayjs/plugin/relativeTime.js"; | ||
| //#region src/PacerContextProvider.tsx | ||
| const initialPacerDevtoolsStore = { | ||
| asyncBatchers: [], | ||
| asyncDebouncers: [], | ||
| asyncQueuers: [], | ||
| asyncRateLimiters: [], | ||
| asyncThrottlers: [], | ||
| batchers: [], | ||
| debouncers: [], | ||
| queuers: [], | ||
| rateLimiters: [], | ||
| throttlers: [], | ||
| lastUpdatedByKey: {} | ||
| }; | ||
| const PacerDevtoolsContext = createContext([initialPacerDevtoolsStore, () => {}]); | ||
| const updateOrAddToArray = (oldArray, newItem) => { | ||
| const index = oldArray.findIndex((item) => item.key === newItem.key); | ||
| if (index !== -1) return oldArray.map((item, i) => i === index ? newItem : item); | ||
| return [...oldArray, newItem]; | ||
| }; | ||
| function PacerContextProvider(props) { | ||
| const [store, setStore] = createStore(initialPacerDevtoolsStore); | ||
| createEffect(() => { | ||
| onCleanup(pacerEventClient.onAllPluginEvents((_e) => { | ||
| const e = _e; | ||
| switch (e.type) { | ||
| case "pacer:AsyncBatcher": | ||
| setStore({ | ||
| asyncBatchers: updateOrAddToArray(store.asyncBatchers, e.payload), | ||
| lastUpdatedByKey: { | ||
| ...store.lastUpdatedByKey, | ||
| [e.payload.key]: Date.now() | ||
| } | ||
| }); | ||
| break; | ||
| case "pacer:AsyncDebouncer": | ||
| setStore({ | ||
| asyncDebouncers: updateOrAddToArray(store.asyncDebouncers, e.payload), | ||
| lastUpdatedByKey: { | ||
| ...store.lastUpdatedByKey, | ||
| [e.payload.key]: Date.now() | ||
| } | ||
| }); | ||
| break; | ||
| case "pacer:AsyncQueuer": | ||
| setStore({ | ||
| asyncQueuers: updateOrAddToArray(store.asyncQueuers, e.payload), | ||
| lastUpdatedByKey: { | ||
| ...store.lastUpdatedByKey, | ||
| [e.payload.key]: Date.now() | ||
| } | ||
| }); | ||
| break; | ||
| case "pacer:AsyncRateLimiter": | ||
| setStore({ | ||
| asyncRateLimiters: updateOrAddToArray(store.asyncRateLimiters, e.payload), | ||
| lastUpdatedByKey: { | ||
| ...store.lastUpdatedByKey, | ||
| [e.payload.key]: Date.now() | ||
| } | ||
| }); | ||
| break; | ||
| case "pacer:AsyncThrottler": | ||
| setStore({ | ||
| asyncThrottlers: updateOrAddToArray(store.asyncThrottlers, e.payload), | ||
| lastUpdatedByKey: { | ||
| ...store.lastUpdatedByKey, | ||
| [e.payload.key]: Date.now() | ||
| } | ||
| }); | ||
| break; | ||
| case "pacer:Batcher": | ||
| setStore({ | ||
| batchers: updateOrAddToArray(store.batchers, e.payload), | ||
| lastUpdatedByKey: { | ||
| ...store.lastUpdatedByKey, | ||
| [e.payload.key]: Date.now() | ||
| } | ||
| }); | ||
| break; | ||
| case "pacer:Debouncer": | ||
| setStore({ | ||
| debouncers: updateOrAddToArray(store.debouncers, e.payload), | ||
| lastUpdatedByKey: { | ||
| ...store.lastUpdatedByKey, | ||
| [e.payload.key]: Date.now() | ||
| } | ||
| }); | ||
| break; | ||
| case "pacer:Queuer": | ||
| setStore({ | ||
| queuers: updateOrAddToArray(store.queuers, e.payload), | ||
| lastUpdatedByKey: { | ||
| ...store.lastUpdatedByKey, | ||
| [e.payload.key]: Date.now() | ||
| } | ||
| }); | ||
| break; | ||
| case "pacer:RateLimiter": | ||
| setStore({ | ||
| rateLimiters: updateOrAddToArray(store.rateLimiters, e.payload), | ||
| lastUpdatedByKey: { | ||
| ...store.lastUpdatedByKey, | ||
| [e.payload.key]: Date.now() | ||
| } | ||
| }); | ||
| break; | ||
| case "pacer:Throttler": | ||
| setStore({ | ||
| throttlers: updateOrAddToArray(store.throttlers, e.payload), | ||
| lastUpdatedByKey: { | ||
| ...store.lastUpdatedByKey, | ||
| [e.payload.key]: Date.now() | ||
| } | ||
| }); | ||
| break; | ||
| } | ||
| })); | ||
| }); | ||
| return createComponent(PacerDevtoolsContext.Provider, { | ||
| value: [store, setStore], | ||
| get children() { | ||
| return props.children; | ||
| } | ||
| }); | ||
| } | ||
| const usePacerDevtoolsContext = () => { | ||
| return useContext(PacerDevtoolsContext); | ||
| }; | ||
| const usePacerDevtoolsState = () => { | ||
| const [state] = usePacerDevtoolsContext(); | ||
| return state; | ||
| }; | ||
| //#endregion | ||
| //#region src/styles/tokens.ts | ||
| const tokens = { | ||
| colors: { | ||
| inherit: "inherit", | ||
| current: "currentColor", | ||
| transparent: "transparent", | ||
| black: "#000000", | ||
| white: "#ffffff", | ||
| neutral: { | ||
| 50: "#f9fafb", | ||
| 100: "#f2f4f7", | ||
| 200: "#eaecf0", | ||
| 300: "#d0d5dd", | ||
| 400: "#98a2b3", | ||
| 500: "#667085", | ||
| 600: "#475467", | ||
| 700: "#344054", | ||
| 800: "#1d2939", | ||
| 900: "#101828" | ||
| }, | ||
| darkGray: { | ||
| 50: "#525c7a", | ||
| 100: "#49536e", | ||
| 200: "#414962", | ||
| 300: "#394056", | ||
| 400: "#313749", | ||
| 500: "#292e3d", | ||
| 600: "#212530", | ||
| 700: "#191c24", | ||
| 800: "#111318", | ||
| 900: "#0b0d10" | ||
| }, | ||
| gray: { | ||
| 50: "#f9fafb", | ||
| 100: "#f2f4f7", | ||
| 200: "#eaecf0", | ||
| 300: "#d0d5dd", | ||
| 400: "#98a2b3", | ||
| 500: "#667085", | ||
| 600: "#475467", | ||
| 700: "#344054", | ||
| 800: "#1d2939", | ||
| 900: "#101828" | ||
| }, | ||
| blue: { | ||
| 25: "#F5FAFF", | ||
| 50: "#EFF8FF", | ||
| 100: "#D1E9FF", | ||
| 200: "#B2DDFF", | ||
| 300: "#84CAFF", | ||
| 400: "#53B1FD", | ||
| 500: "#2E90FA", | ||
| 600: "#1570EF", | ||
| 700: "#175CD3", | ||
| 800: "#1849A9", | ||
| 900: "#194185" | ||
| }, | ||
| green: { | ||
| 25: "#F6FEF9", | ||
| 50: "#ECFDF3", | ||
| 100: "#D1FADF", | ||
| 200: "#A6F4C5", | ||
| 300: "#6CE9A6", | ||
| 400: "#32D583", | ||
| 500: "#12B76A", | ||
| 600: "#039855", | ||
| 700: "#027A48", | ||
| 800: "#05603A", | ||
| 900: "#054F31" | ||
| }, | ||
| red: { | ||
| 50: "#fef2f2", | ||
| 100: "#fee2e2", | ||
| 200: "#fecaca", | ||
| 300: "#fca5a5", | ||
| 400: "#f87171", | ||
| 500: "#ef4444", | ||
| 600: "#dc2626", | ||
| 700: "#b91c1c", | ||
| 800: "#991b1b", | ||
| 900: "#7f1d1d", | ||
| 950: "#450a0a" | ||
| }, | ||
| yellow: { | ||
| 25: "#FFFCF5", | ||
| 50: "#FFFAEB", | ||
| 100: "#FEF0C7", | ||
| 200: "#FEDF89", | ||
| 300: "#FEC84B", | ||
| 400: "#FDB022", | ||
| 500: "#F79009", | ||
| 600: "#DC6803", | ||
| 700: "#B54708", | ||
| 800: "#93370D", | ||
| 900: "#7A2E0E" | ||
| }, | ||
| purple: { | ||
| 25: "#FAFAFF", | ||
| 50: "#F4F3FF", | ||
| 100: "#EBE9FE", | ||
| 200: "#D9D6FE", | ||
| 300: "#BDB4FE", | ||
| 400: "#9B8AFB", | ||
| 500: "#7A5AF8", | ||
| 600: "#6938EF", | ||
| 700: "#5925DC", | ||
| 800: "#4A1FB8", | ||
| 900: "#3E1C96" | ||
| }, | ||
| teal: { | ||
| 25: "#F6FEFC", | ||
| 50: "#F0FDF9", | ||
| 100: "#CCFBEF", | ||
| 200: "#99F6E0", | ||
| 300: "#5FE9D0", | ||
| 400: "#2ED3B7", | ||
| 500: "#15B79E", | ||
| 600: "#0E9384", | ||
| 700: "#107569", | ||
| 800: "#125D56", | ||
| 900: "#134E48" | ||
| }, | ||
| pink: { | ||
| 25: "#fdf2f8", | ||
| 50: "#fce7f3", | ||
| 100: "#fbcfe8", | ||
| 200: "#f9a8d4", | ||
| 300: "#f472b6", | ||
| 400: "#ec4899", | ||
| 500: "#db2777", | ||
| 600: "#be185d", | ||
| 700: "#9d174d", | ||
| 800: "#831843", | ||
| 900: "#500724" | ||
| }, | ||
| cyan: { | ||
| 25: "#ecfeff", | ||
| 50: "#cffafe", | ||
| 100: "#a5f3fc", | ||
| 200: "#67e8f9", | ||
| 300: "#22d3ee", | ||
| 400: "#06b6d4", | ||
| 500: "#0891b2", | ||
| 600: "#0e7490", | ||
| 700: "#155e75", | ||
| 800: "#164e63", | ||
| 900: "#083344" | ||
| } | ||
| }, | ||
| alpha: { | ||
| 100: "ff", | ||
| 90: "e5", | ||
| 80: "cc", | ||
| 70: "b3", | ||
| 60: "99", | ||
| 50: "80", | ||
| 40: "66", | ||
| 30: "4d", | ||
| 20: "33", | ||
| 10: "1a", | ||
| 0: "00" | ||
| }, | ||
| font: { | ||
| size: { | ||
| "2xs": "calc(var(--tsrd-font-size) * 0.625)", | ||
| xs: "calc(var(--tsrd-font-size) * 0.75)", | ||
| sm: "calc(var(--tsrd-font-size) * 0.875)", | ||
| md: "var(--tsrd-font-size)", | ||
| lg: "calc(var(--tsrd-font-size) * 1.125)", | ||
| xl: "calc(var(--tsrd-font-size) * 1.25)", | ||
| "2xl": "calc(var(--tsrd-font-size) * 1.5)", | ||
| "3xl": "calc(var(--tsrd-font-size) * 1.875)", | ||
| "4xl": "calc(var(--tsrd-font-size) * 2.25)", | ||
| "5xl": "calc(var(--tsrd-font-size) * 3)", | ||
| "6xl": "calc(var(--tsrd-font-size) * 3.75)", | ||
| "7xl": "calc(var(--tsrd-font-size) * 4.5)", | ||
| "8xl": "calc(var(--tsrd-font-size) * 6)", | ||
| "9xl": "calc(var(--tsrd-font-size) * 8)" | ||
| }, | ||
| lineHeight: { | ||
| "3xs": "calc(var(--tsrd-font-size) * 0.75)", | ||
| "2xs": "calc(var(--tsrd-font-size) * 0.875)", | ||
| xs: "calc(var(--tsrd-font-size) * 1)", | ||
| sm: "calc(var(--tsrd-font-size) * 1.25)", | ||
| md: "calc(var(--tsrd-font-size) * 1.5)", | ||
| lg: "calc(var(--tsrd-font-size) * 1.75)", | ||
| xl: "calc(var(--tsrd-font-size) * 2)", | ||
| "2xl": "calc(var(--tsrd-font-size) * 2.25)", | ||
| "3xl": "calc(var(--tsrd-font-size) * 2.5)", | ||
| "4xl": "calc(var(--tsrd-font-size) * 2.75)", | ||
| "5xl": "calc(var(--tsrd-font-size) * 3)", | ||
| "6xl": "calc(var(--tsrd-font-size) * 3.25)", | ||
| "7xl": "calc(var(--tsrd-font-size) * 3.5)", | ||
| "8xl": "calc(var(--tsrd-font-size) * 3.75)", | ||
| "9xl": "calc(var(--tsrd-font-size) * 4)" | ||
| }, | ||
| weight: { | ||
| thin: "100", | ||
| extralight: "200", | ||
| light: "300", | ||
| normal: "400", | ||
| medium: "500", | ||
| semibold: "600", | ||
| bold: "700", | ||
| extrabold: "800", | ||
| black: "900" | ||
| }, | ||
| fontFamily: { | ||
| sans: "ui-sans-serif, Inter, system-ui, sans-serif, sans-serif", | ||
| mono: `ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace` | ||
| } | ||
| }, | ||
| breakpoints: { | ||
| xs: "320px", | ||
| sm: "640px", | ||
| md: "768px", | ||
| lg: "1024px", | ||
| xl: "1280px", | ||
| "2xl": "1536px" | ||
| }, | ||
| border: { radius: { | ||
| none: "0px", | ||
| xs: "calc(var(--tsrd-font-size) * 0.125)", | ||
| sm: "calc(var(--tsrd-font-size) * 0.25)", | ||
| md: "calc(var(--tsrd-font-size) * 0.375)", | ||
| lg: "calc(var(--tsrd-font-size) * 0.5)", | ||
| xl: "calc(var(--tsrd-font-size) * 0.75)", | ||
| "2xl": "calc(var(--tsrd-font-size) * 1)", | ||
| "3xl": "calc(var(--tsrd-font-size) * 1.5)", | ||
| full: "9999px" | ||
| } }, | ||
| size: { | ||
| 0: "0px", | ||
| .25: "calc(var(--tsrd-font-size) * 0.0625)", | ||
| .5: "calc(var(--tsrd-font-size) * 0.125)", | ||
| 1: "calc(var(--tsrd-font-size) * 0.25)", | ||
| 1.5: "calc(var(--tsrd-font-size) * 0.375)", | ||
| 2: "calc(var(--tsrd-font-size) * 0.5)", | ||
| 2.5: "calc(var(--tsrd-font-size) * 0.625)", | ||
| 3: "calc(var(--tsrd-font-size) * 0.75)", | ||
| 3.5: "calc(var(--tsrd-font-size) * 0.875)", | ||
| 4: "calc(var(--tsrd-font-size) * 1)", | ||
| 4.5: "calc(var(--tsrd-font-size) * 1.125)", | ||
| 5: "calc(var(--tsrd-font-size) * 1.25)", | ||
| 5.5: "calc(var(--tsrd-font-size) * 1.375)", | ||
| 6: "calc(var(--tsrd-font-size) * 1.5)", | ||
| 6.5: "calc(var(--tsrd-font-size) * 1.625)", | ||
| 7: "calc(var(--tsrd-font-size) * 1.75)", | ||
| 8: "calc(var(--tsrd-font-size) * 2)", | ||
| 9: "calc(var(--tsrd-font-size) * 2.25)", | ||
| 10: "calc(var(--tsrd-font-size) * 2.5)", | ||
| 11: "calc(var(--tsrd-font-size) * 2.75)", | ||
| 12: "calc(var(--tsrd-font-size) * 3)", | ||
| 14: "calc(var(--tsrd-font-size) * 3.5)", | ||
| 16: "calc(var(--tsrd-font-size) * 4)", | ||
| 20: "calc(var(--tsrd-font-size) * 5)", | ||
| 24: "calc(var(--tsrd-font-size) * 6)", | ||
| 28: "calc(var(--tsrd-font-size) * 7)", | ||
| 32: "calc(var(--tsrd-font-size) * 8)", | ||
| 36: "calc(var(--tsrd-font-size) * 9)", | ||
| 40: "calc(var(--tsrd-font-size) * 10)", | ||
| 44: "calc(var(--tsrd-font-size) * 11)", | ||
| 48: "calc(var(--tsrd-font-size) * 12)", | ||
| 52: "calc(var(--tsrd-font-size) * 13)", | ||
| 56: "calc(var(--tsrd-font-size) * 14)", | ||
| 60: "calc(var(--tsrd-font-size) * 15)", | ||
| 64: "calc(var(--tsrd-font-size) * 16)", | ||
| 72: "calc(var(--tsrd-font-size) * 18)", | ||
| 80: "calc(var(--tsrd-font-size) * 20)", | ||
| 96: "calc(var(--tsrd-font-size) * 24)" | ||
| }, | ||
| shadow: { | ||
| xs: (_ = "rgb(0 0 0 / 0.1)") => `0 1px 2px 0 rgb(0 0 0 / 0.05)`, | ||
| sm: (color = "rgb(0 0 0 / 0.1)") => `0 1px 3px 0 ${color}, 0 1px 2px -1px ${color}`, | ||
| md: (color = "rgb(0 0 0 / 0.1)") => `0 4px 6px -1px ${color}, 0 2px 4px -2px ${color}`, | ||
| lg: (color = "rgb(0 0 0 / 0.1)") => `0 10px 15px -3px ${color}, 0 4px 6px -4px ${color}`, | ||
| xl: (color = "rgb(0 0 0 / 0.1)") => `0 20px 25px -5px ${color}, 0 8px 10px -6px ${color}`, | ||
| "2xl": (color = "rgb(0 0 0 / 0.25)") => `0 25px 50px -12px ${color}`, | ||
| inner: (color = "rgb(0 0 0 / 0.05)") => `inset 0 2px 4px 0 ${color}`, | ||
| none: () => `none` | ||
| }, | ||
| zIndices: { | ||
| hide: -1, | ||
| auto: "auto", | ||
| base: 0, | ||
| docked: 10, | ||
| dropdown: 1e3, | ||
| sticky: 1100, | ||
| banner: 1200, | ||
| overlay: 1300, | ||
| modal: 1400, | ||
| popover: 1500, | ||
| skipLink: 1600, | ||
| toast: 1700, | ||
| tooltip: 1800 | ||
| } | ||
| }; | ||
| //#endregion | ||
| //#region src/styles/use-styles.ts | ||
| const stylesFactory = (theme) => { | ||
| const { colors, font, size, alpha, border } = tokens; | ||
| const { fontFamily, size: fontSize } = font; | ||
| const css = goober.css; | ||
| const t = (light, dark) => theme === "light" ? light : dark; | ||
| return { | ||
| mainContainer: css` | ||
| display: flex; | ||
| flex: 1; | ||
| min-height: 0; | ||
| overflow: hidden; | ||
| padding: ${size[2]}; | ||
| padding-top: 0; | ||
| margin-top: ${size[2]}; | ||
| `, | ||
| dragHandle: css` | ||
| width: 8px; | ||
| background: ${t(colors.gray[300], colors.darkGray[600])}; | ||
| cursor: col-resize; | ||
| position: relative; | ||
| transition: all 0.2s ease; | ||
| user-select: none; | ||
| pointer-events: all; | ||
| margin: 0 ${size[1]}; | ||
| border-radius: 2px; | ||
| &:hover { | ||
| background: ${t(colors.blue[600], colors.blue[500])}; | ||
| margin: 0 ${size[1]}; | ||
| } | ||
| &.dragging { | ||
| background: ${t(colors.blue[700], colors.blue[600])}; | ||
| margin: 0 ${size[1]}; | ||
| } | ||
| &::after { | ||
| content: ''; | ||
| position: absolute; | ||
| top: 50%; | ||
| left: 50%; | ||
| transform: translate(-50%, -50%); | ||
| width: 2px; | ||
| height: 20px; | ||
| background: ${t(colors.gray[400], colors.darkGray[400])}; | ||
| border-radius: 1px; | ||
| pointer-events: none; | ||
| } | ||
| &:hover::after, | ||
| &.dragging::after { | ||
| background: ${t(colors.blue[500], colors.blue[300])}; | ||
| } | ||
| `, | ||
| leftPanel: css` | ||
| background: ${t(colors.gray[100], colors.darkGray[800])}; | ||
| border-radius: ${border.radius.lg}; | ||
| border: 1px solid ${t(colors.gray[200], colors.darkGray[700])}; | ||
| display: flex; | ||
| flex-direction: column; | ||
| overflow: hidden; | ||
| min-height: 0; | ||
| flex-shrink: 0; | ||
| `, | ||
| rightPanel: css` | ||
| background: ${t(colors.gray[100], colors.darkGray[800])}; | ||
| border-radius: ${border.radius.lg}; | ||
| border: 1px solid ${t(colors.gray[200], colors.darkGray[700])}; | ||
| display: flex; | ||
| flex-direction: column; | ||
| overflow: hidden; | ||
| min-height: 0; | ||
| flex: 1; | ||
| `, | ||
| panelHeader: css` | ||
| font-size: ${fontSize.md}; | ||
| font-weight: ${font.weight.bold}; | ||
| color: ${t(colors.blue[700], colors.blue[400])}; | ||
| padding: ${size[2]}; | ||
| border-bottom: 1px solid ${t(colors.gray[200], colors.darkGray[700])}; | ||
| background: ${t(colors.gray[100], colors.darkGray[800])}; | ||
| flex-shrink: 0; | ||
| `, | ||
| utilList: css` | ||
| flex: 1; | ||
| overflow-y: auto; | ||
| padding: ${size[1]}; | ||
| min-height: 0; | ||
| `, | ||
| utilGroup: css` | ||
| margin-bottom: ${size[2]}; | ||
| `, | ||
| utilGroupHeader: css` | ||
| font-size: ${fontSize.xs}; | ||
| font-weight: ${font.weight.semibold}; | ||
| color: ${t(colors.gray[600], colors.gray[400])}; | ||
| text-transform: uppercase; | ||
| letter-spacing: 0.05em; | ||
| margin-bottom: ${size[1]}; | ||
| padding: ${size[1]} ${size[2]}; | ||
| background: ${t(colors.gray[200], colors.darkGray[700])}; | ||
| border-radius: ${border.radius.md}; | ||
| `, | ||
| utilRow: css` | ||
| display: flex; | ||
| justify-content: space-between; | ||
| align-items: center; | ||
| padding: ${size[2]}; | ||
| margin-bottom: ${size[1]}; | ||
| background: ${t(colors.gray[200], colors.darkGray[700])}; | ||
| border-radius: ${border.radius.md}; | ||
| cursor: pointer; | ||
| transition: all 0.2s ease; | ||
| border: 1px solid transparent; | ||
| &:hover { | ||
| background: ${t(colors.gray[300], colors.darkGray[600])}; | ||
| border-color: ${t(colors.gray[400], colors.darkGray[500])}; | ||
| } | ||
| `, | ||
| utilRowSelected: css` | ||
| background: ${t(colors.blue[100], colors.blue[900] + alpha[20])}; | ||
| border-color: ${t(colors.blue[600], colors.blue[500])}; | ||
| box-shadow: 0 0 0 1px | ||
| ${t(colors.blue[600] + alpha[30], colors.blue[500] + alpha[30])}; | ||
| `, | ||
| utilKey: css` | ||
| font-family: ${fontFamily.mono}; | ||
| font-size: ${fontSize.xs}; | ||
| color: ${t(colors.gray[900], colors.gray[100])}; | ||
| flex: 1; | ||
| overflow: hidden; | ||
| text-overflow: ellipsis; | ||
| white-space: nowrap; | ||
| `, | ||
| utilStatus: css` | ||
| font-size: ${fontSize.xs}; | ||
| color: ${t(colors.gray[600], colors.gray[400])}; | ||
| text-transform: uppercase; | ||
| letter-spacing: 0.05em; | ||
| padding: ${size[1]} ${size[1]}; | ||
| background: ${t(colors.gray[300], colors.darkGray[600])}; | ||
| border-radius: ${border.radius.sm}; | ||
| margin-left: ${size[1]}; | ||
| `, | ||
| stateDetails: css` | ||
| flex: 1; | ||
| overflow-y: auto; | ||
| padding: ${size[2]}; | ||
| min-height: 0; | ||
| `, | ||
| stateHeader: css` | ||
| margin-bottom: ${size[2]}; | ||
| padding-bottom: ${size[2]}; | ||
| border-bottom: 1px solid ${t(colors.gray[200], colors.darkGray[700])}; | ||
| `, | ||
| stateTitle: css` | ||
| font-size: ${fontSize.md}; | ||
| font-weight: ${font.weight.bold}; | ||
| color: ${t(colors.blue[700], colors.blue[400])}; | ||
| margin-bottom: ${size[1]}; | ||
| `, | ||
| stateKey: css` | ||
| font-family: ${fontFamily.mono}; | ||
| font-size: ${fontSize.xs}; | ||
| color: ${t(colors.gray[600], colors.gray[400])}; | ||
| word-break: break-all; | ||
| `, | ||
| stateContent: css` | ||
| background: ${t(colors.gray[100], colors.darkGray[700])}; | ||
| border-radius: ${border.radius.md}; | ||
| padding: ${size[2]}; | ||
| border: 1px solid ${t(colors.gray[300], colors.darkGray[600])}; | ||
| `, | ||
| detailsGrid: css` | ||
| display: grid; | ||
| grid-template-columns: 1fr; | ||
| gap: ${size[2]}; | ||
| align-items: start; | ||
| `, | ||
| detailSection: css` | ||
| background: ${t(colors.white, colors.darkGray[700])}; | ||
| border: 1px solid ${t(colors.gray[300], colors.darkGray[600])}; | ||
| border-radius: ${border.radius.md}; | ||
| padding: ${size[2]}; | ||
| `, | ||
| detailSectionHeader: css` | ||
| font-size: ${fontSize.sm}; | ||
| font-weight: ${font.weight.bold}; | ||
| color: ${t(colors.gray[800], colors.gray[200])}; | ||
| margin-bottom: ${size[1]}; | ||
| text-transform: uppercase; | ||
| letter-spacing: 0.04em; | ||
| `, | ||
| actionsRow: css` | ||
| display: flex; | ||
| flex-wrap: wrap; | ||
| gap: ${size[2]}; | ||
| `, | ||
| actionButton: css` | ||
| display: inline-flex; | ||
| align-items: center; | ||
| gap: ${size[1]}; | ||
| padding: ${size[1]} ${size[2]}; | ||
| border-radius: ${border.radius.md}; | ||
| border: 1px solid ${t(colors.gray[300], colors.darkGray[500])}; | ||
| background: ${t(colors.gray[200], colors.darkGray[600])}; | ||
| color: ${t(colors.gray[900], colors.gray[100])}; | ||
| font-size: ${fontSize.xs}; | ||
| cursor: pointer; | ||
| user-select: none; | ||
| transition: | ||
| background 0.15s, | ||
| border-color 0.15s; | ||
| &:hover { | ||
| background: ${t(colors.gray[300], colors.darkGray[500])}; | ||
| border-color: ${t(colors.gray[400], colors.darkGray[400])}; | ||
| } | ||
| &:disabled { | ||
| opacity: 0.5; | ||
| cursor: not-allowed; | ||
| &:hover { | ||
| background: ${t(colors.gray[200], colors.darkGray[600])}; | ||
| border-color: ${t(colors.gray[300], colors.darkGray[500])}; | ||
| } | ||
| } | ||
| `, | ||
| actionDotBlue: css` | ||
| width: 6px; | ||
| height: 6px; | ||
| border-radius: 9999px; | ||
| background: ${colors.blue[400]}; | ||
| `, | ||
| actionDotGreen: css` | ||
| width: 6px; | ||
| height: 6px; | ||
| border-radius: 9999px; | ||
| background: ${colors.green[400]}; | ||
| `, | ||
| actionDotRed: css` | ||
| width: 6px; | ||
| height: 6px; | ||
| border-radius: 9999px; | ||
| background: ${colors.red[400]}; | ||
| `, | ||
| actionDotYellow: css` | ||
| width: 6px; | ||
| height: 6px; | ||
| border-radius: 9999px; | ||
| background: ${colors.yellow[400]}; | ||
| `, | ||
| actionDotOrange: css` | ||
| width: 6px; | ||
| height: 6px; | ||
| border-radius: 9999px; | ||
| background: ${colors.pink[400]}; | ||
| `, | ||
| actionDotPurple: css` | ||
| width: 6px; | ||
| height: 6px; | ||
| border-radius: 9999px; | ||
| background: ${colors.purple[400]}; | ||
| `, | ||
| infoGrid: css` | ||
| display: grid; | ||
| grid-template-columns: auto 1fr; | ||
| gap: ${size[1]}; | ||
| row-gap: ${size[1]}; | ||
| align-items: center; | ||
| `, | ||
| infoLabel: css` | ||
| color: ${t(colors.gray[600], colors.gray[400])}; | ||
| font-size: ${fontSize.xs}; | ||
| text-transform: uppercase; | ||
| letter-spacing: 0.05em; | ||
| `, | ||
| infoValueMono: css` | ||
| font-family: ${fontFamily.mono}; | ||
| font-size: ${fontSize.xs}; | ||
| color: ${t(colors.gray[900], colors.gray[100])}; | ||
| word-break: break-all; | ||
| `, | ||
| noSelection: css` | ||
| flex: 1; | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: center; | ||
| color: ${t(colors.gray[500], colors.gray[500])}; | ||
| font-style: italic; | ||
| text-align: center; | ||
| padding: ${size[4]}; | ||
| `, | ||
| sectionContainer: css` | ||
| display: flex; | ||
| flex-wrap: wrap; | ||
| gap: ${size[4]}; | ||
| `, | ||
| section: css` | ||
| background: ${t(colors.gray[100], colors.darkGray[800])}; | ||
| border-radius: ${border.radius.lg}; | ||
| box-shadow: ${tokens.shadow.md(t(colors.gray[400] + alpha[80], colors.black + alpha[80]))}; | ||
| padding: ${size[4]}; | ||
| margin-bottom: ${size[4]}; | ||
| border: 1px solid ${t(colors.gray[200], colors.darkGray[700])}; | ||
| min-width: 0; | ||
| max-width: 33%; | ||
| max-height: fit-content; | ||
| `, | ||
| sectionHeader: css` | ||
| font-size: ${fontSize.lg}; | ||
| font-weight: ${font.weight.bold}; | ||
| margin-bottom: ${size[2]}; | ||
| color: ${t(colors.blue[600], colors.blue[400])}; | ||
| letter-spacing: 0.01em; | ||
| display: flex; | ||
| align-items: center; | ||
| gap: ${size[2]}; | ||
| `, | ||
| sectionEmpty: css` | ||
| color: ${t(colors.gray[500], colors.gray[500])}; | ||
| font-size: ${fontSize.sm}; | ||
| font-style: italic; | ||
| margin: ${size[2]} 0; | ||
| `, | ||
| instanceList: css` | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: ${size[2]}; | ||
| background: ${t(colors.gray[200], colors.darkGray[700])}; | ||
| border: 1px solid ${t(colors.gray[300], colors.darkGray[600])}; | ||
| `, | ||
| instanceCard: css` | ||
| background: ${t(colors.gray[200], colors.darkGray[700])}; | ||
| border-radius: ${border.radius.md}; | ||
| padding: ${size[3]}; | ||
| border: 1px solid ${t(colors.gray[300], colors.darkGray[600])}; | ||
| font-size: ${fontSize.sm}; | ||
| color: ${t(colors.gray[900], colors.gray[100])}; | ||
| font-family: ${fontFamily.mono}; | ||
| overflow-x: auto; | ||
| transition: | ||
| box-shadow 0.3s, | ||
| background 0.3s; | ||
| ` | ||
| }; | ||
| }; | ||
| function useStyles() { | ||
| const { theme } = useTheme(); | ||
| const [styles, setStyles] = createSignal(stylesFactory(theme())); | ||
| createEffect(() => { | ||
| setStyles(stylesFactory(theme())); | ||
| }); | ||
| return styles; | ||
| } | ||
| //#endregion | ||
| //#region src/components/util-groups.ts | ||
| const UTIL_GROUPS = [ | ||
| { | ||
| key: "debouncers", | ||
| label: "Debouncers", | ||
| displayName: "Debouncer" | ||
| }, | ||
| { | ||
| key: "throttlers", | ||
| label: "Throttlers", | ||
| displayName: "Throttler" | ||
| }, | ||
| { | ||
| key: "batchers", | ||
| label: "Batchers", | ||
| displayName: "Batcher" | ||
| }, | ||
| { | ||
| key: "queuers", | ||
| label: "Queuers", | ||
| displayName: "Queuer" | ||
| }, | ||
| { | ||
| key: "rateLimiters", | ||
| label: "Rate Limiters", | ||
| displayName: "Rate Limiter" | ||
| }, | ||
| { | ||
| key: "asyncDebouncers", | ||
| label: "Async Debouncers", | ||
| displayName: "Async Debouncer" | ||
| }, | ||
| { | ||
| key: "asyncThrottlers", | ||
| label: "Async Throttlers", | ||
| displayName: "Async Throttler" | ||
| }, | ||
| { | ||
| key: "asyncBatchers", | ||
| label: "Async Batchers", | ||
| displayName: "Async Batcher" | ||
| }, | ||
| { | ||
| key: "asyncQueuers", | ||
| label: "Async Queuers", | ||
| displayName: "Async Queuer" | ||
| }, | ||
| { | ||
| key: "asyncRateLimiters", | ||
| label: "Async Rate Limiters", | ||
| displayName: "Async Rate Limiter" | ||
| } | ||
| ]; | ||
| //#endregion | ||
| //#region src/components/UtilList.tsx | ||
| var _tmpl$$4 = [ | ||
| "<div", | ||
| ">", | ||
| "</div>" | ||
| ], _tmpl$2$2 = [ | ||
| "<div", | ||
| "><div", | ||
| ">", | ||
| "</div>", | ||
| "</div>" | ||
| ], _tmpl$3$2 = [ | ||
| "<div", | ||
| "><div", | ||
| ">", | ||
| "</div><div", | ||
| ">", | ||
| "</div></div>" | ||
| ]; | ||
| function UtilList(props) { | ||
| const styles = useStyles(); | ||
| return ssr(_tmpl$$4, ssrAttribute("class", escape(styles().utilList, true), false), escape(createComponent(For, { | ||
| each: UTIL_GROUPS, | ||
| children: (group) => props.utilState()[group.key].length > 0 && ssr(_tmpl$2$2, ssrAttribute("class", escape(styles().utilGroup, true), false), ssrAttribute("class", escape(styles().utilGroupHeader, true), false), escape(group.label), escape(createComponent(For, { | ||
| get each() { | ||
| return props.utilState()[group.key]; | ||
| }, | ||
| children: (instance) => { | ||
| const status = (() => { | ||
| try { | ||
| const statusAccessor = useStore(instance.store, (s) => s.status); | ||
| return () => statusAccessor() ?? "unknown"; | ||
| } catch { | ||
| return () => "unknown"; | ||
| } | ||
| })(); | ||
| return ssr(_tmpl$3$2, ssrAttribute("class", escape(clsx(styles().utilRow, props.selectedKey() === instance.key && styles().utilRowSelected), true), false), ssrAttribute("class", escape(styles().utilKey, true), false), escape(instance.key), ssrAttribute("class", escape(styles().utilStatus, true), false), escape(status())); | ||
| } | ||
| }))) | ||
| }))); | ||
| } | ||
| //#endregion | ||
| //#region src/components/ActionButtons.tsx | ||
| var _tmpl$$3 = ["<div", ">No actions available for this util</div>"], _tmpl$2$1 = [ | ||
| "<div", | ||
| ">", | ||
| "", | ||
| "", | ||
| "", | ||
| "", | ||
| "", | ||
| "</div>" | ||
| ], _tmpl$3$1 = [ | ||
| "<button", | ||
| "><span", | ||
| "></span>", | ||
| "</button>" | ||
| ], _tmpl$4 = [ | ||
| "<button", | ||
| "", | ||
| "><span", | ||
| "></span>Flush</button>" | ||
| ], _tmpl$5 = [ | ||
| "<button", | ||
| "", | ||
| "><span", | ||
| "></span>Cancel</button>" | ||
| ], _tmpl$6 = [ | ||
| "<button", | ||
| "><span", | ||
| "></span>Reset</button>" | ||
| ], _tmpl$7 = [ | ||
| "<button", | ||
| "", | ||
| "><span", | ||
| "></span>Clear</button>" | ||
| ]; | ||
| function ActionButtons(props) { | ||
| const styles = useStyles(); | ||
| const utilInstance = props.instance; | ||
| const utilState = utilInstance?.store?.state; | ||
| const hasPending = utilState && "isPending" in utilState; | ||
| const hasEmpty = utilState && "isEmpty" in utilState; | ||
| const isPending = hasPending ? !!utilState.isPending : false; | ||
| const isEmpty = hasEmpty ? utilState.isEmpty : void 0; | ||
| const hasFlush = typeof utilInstance.flush === "function"; | ||
| const hasCancel = typeof utilInstance.cancel === "function"; | ||
| const hasReset = typeof utilInstance.reset === "function"; | ||
| const hasClear = typeof utilInstance.clear === "function"; | ||
| const hasStart = typeof utilInstance.start === "function"; | ||
| const hasStop = typeof utilInstance.stop === "function"; | ||
| const hasStartStop = hasStart && hasStop; | ||
| const isRunning = utilState?.isRunning ?? true; | ||
| if (!hasPending && !hasFlush && !hasCancel && !hasReset && !hasClear) return ssr(_tmpl$$3, ssrAttribute("class", escape(styles().sectionEmpty, true), false)); | ||
| `${props.utilName}`; | ||
| return ssr(_tmpl$2$1, ssrAttribute("class", escape(styles().actionsRow, true), false), hasPending && ssr(_tmpl$3$1, ssrAttribute("class", escape(styles().actionButton, true), false), ssrAttribute("class", escape(styles().actionDotBlue, true), false), isPending ? "Restore Pending" : "Trigger Pending"), hasFlush && ssr(_tmpl$4, ssrAttribute("class", escape(styles().actionButton, true), false), ssrAttribute("disabled", props.utilName.toLowerCase().includes("debouncer") && !isPending || props.utilName.toLowerCase().includes("throttler") && !isPending || props.utilName.toLowerCase().includes("batcher") && isEmpty || props.utilName.toLowerCase().includes("queuer") && isEmpty, true), ssrAttribute("class", escape(styles().actionDotGreen, true), false)), hasCancel && ssr(_tmpl$5, ssrAttribute("class", escape(styles().actionButton, true), false), ssrAttribute("disabled", props.utilName.toLowerCase().includes("debouncer") && !isPending || props.utilName.toLowerCase().includes("throttler") && !isPending || props.utilName.toLowerCase().includes("batcher") && isEmpty || props.utilName.toLowerCase().includes("queuer") && isEmpty, true), ssrAttribute("class", escape(styles().actionDotRed, true), false)), hasReset && ssr(_tmpl$6, ssrAttribute("class", escape(styles().actionButton, true), false), ssrAttribute("class", escape(styles().actionDotYellow, true), false)), hasClear && ssr(_tmpl$7, ssrAttribute("class", escape(styles().actionButton, true), false), ssrAttribute("disabled", props.utilName.toLowerCase().includes("batcher") && isEmpty || props.utilName.toLowerCase().includes("queuer") && isEmpty, true), ssrAttribute("class", escape(styles().actionDotOrange, true), false)), hasStartStop && ssr(_tmpl$3$1, ssrAttribute("class", escape(styles().actionButton, true), false), ssrAttribute("class", escape(styles().actionDotPurple, true), false), isRunning ? "Stop" : "Start")); | ||
| } | ||
| //#endregion | ||
| //#region src/components/StateHeader.tsx | ||
| var _tmpl$$2 = [ | ||
| "<div", | ||
| "><div", | ||
| ">", | ||
| "</div><div style=\"", | ||
| "\"><div", | ||
| "><div", | ||
| ">Key</div><div", | ||
| ">", | ||
| "</div><div", | ||
| ">Last Updated</div><div", | ||
| ">", | ||
| " (", | ||
| ")</div></div><div", | ||
| " style=\"", | ||
| "\">", | ||
| "% reduction</div></div></div>" | ||
| ]; | ||
| dayjs.extend(relativeTime); | ||
| function StateHeader(props) { | ||
| const styles = useStyles(); | ||
| const [now, setNow] = createSignal(Date.now()); | ||
| onMount(() => { | ||
| const interval = setInterval(() => { | ||
| setNow(Date.now()); | ||
| }, 1e3); | ||
| onCleanup(() => { | ||
| clearInterval(interval); | ||
| }); | ||
| }); | ||
| const entry = props.selectedInstance(); | ||
| if (!entry) return null; | ||
| const key = entry.instance.key; | ||
| const updatedAt = props.utilState().lastUpdatedByKey[key] ?? Date.now(); | ||
| const getRelativeTime = () => { | ||
| const diffMs = now() - updatedAt; | ||
| const diffSeconds = Math.floor(diffMs / 1e3); | ||
| if (diffSeconds < 60) return `${diffSeconds} second${diffSeconds !== 1 ? "s" : ""} ago`; | ||
| return dayjs(updatedAt).fromNow(); | ||
| }; | ||
| const getReductionPercentage = () => { | ||
| const state = entry.instance.store?.state || entry.instance.state; | ||
| if (!state) return 0; | ||
| const completedExecutions = entry.type.toLowerCase().includes("async") ? state.settleCount || 0 : state.executionCount || 0; | ||
| if (entry.type.toLowerCase().includes("batcher")) { | ||
| const totalItemsProcessed = state.totalItemsProcessed || 0; | ||
| if (totalItemsProcessed === 0) return 0; | ||
| return Math.round((totalItemsProcessed - completedExecutions) / totalItemsProcessed * 100); | ||
| } | ||
| let requestCount = 0; | ||
| if (state.maybeExecuteCount !== void 0) requestCount = state.maybeExecuteCount; | ||
| else if (state.addItemCount !== void 0) requestCount = state.addItemCount; | ||
| else return 0; | ||
| if (requestCount === 0) return 0; | ||
| const reduction = requestCount - completedExecutions; | ||
| return Math.round(reduction / requestCount * 100); | ||
| }; | ||
| const reductionPercentage = getReductionPercentage(); | ||
| return ssr(_tmpl$$2, ssrAttribute("class", escape(styles().stateHeader, true), false), ssrAttribute("class", escape(styles().stateTitle, true), false), escape(entry.type), ssrStyleProperty("display:", "flex") + ssrStyleProperty(";align-items:", "center") + ssrStyleProperty(";gap:", "16px"), ssrAttribute("class", escape(styles().infoGrid, true), false), ssrAttribute("class", escape(styles().infoLabel, true), false), ssrAttribute("class", escape(styles().infoValueMono, true), false), escape(key), ssrAttribute("class", escape(styles().infoLabel, true), false), ssrAttribute("class", escape(styles().infoValueMono, true), false), escape(new Date(updatedAt).toLocaleTimeString()), escape(getRelativeTime()), ssrAttribute("class", escape(styles().infoValueMono, true), false), ssrStyleProperty("margin-left:", "auto") + ssrStyleProperty(";font-weight:", "bold"), escape(reductionPercentage)); | ||
| } | ||
| //#endregion | ||
| //#region src/components/DetailsPanel.tsx | ||
| var _tmpl$$1 = [ | ||
| "<div", | ||
| ">", | ||
| "</div>" | ||
| ], _tmpl$2 = ["<div", ">Select a util from the left panel to view its state</div>"], _tmpl$3 = [ | ||
| "<div", | ||
| "><div", | ||
| "><div", | ||
| ">Actions</div>", | ||
| "</div><div", | ||
| "><div", | ||
| ">State</div><div", | ||
| ">", | ||
| "</div></div><div", | ||
| "><div", | ||
| ">Options</div><div", | ||
| ">", | ||
| "</div></div></div>" | ||
| ]; | ||
| function DetailsPanel(props) { | ||
| const styles = useStyles(); | ||
| return ssr(_tmpl$$1, ssrAttribute("class", escape(styles().stateDetails, true), false), (() => { | ||
| const entry = props.selectedInstance(); | ||
| if (!entry) return ssr(_tmpl$2, ssrAttribute("class", escape(styles().noSelection, true), false)); | ||
| return escape([createComponent(StateHeader, { | ||
| get selectedInstance() { | ||
| return props.selectedInstance; | ||
| }, | ||
| get utilState() { | ||
| return props.utilState; | ||
| } | ||
| }), ssr(_tmpl$3, ssrAttribute("class", escape(styles().detailsGrid, true), false), ssrAttribute("class", escape(styles().detailSection, true), false), ssrAttribute("class", escape(styles().detailSectionHeader, true), false), escape(createComponent(ActionButtons, { | ||
| get instance() { | ||
| return entry.instance; | ||
| }, | ||
| get utilName() { | ||
| return entry.type; | ||
| } | ||
| })), ssrAttribute("class", escape(styles().detailSection, true), false), ssrAttribute("class", escape(styles().detailSectionHeader, true), false), ssrAttribute("class", escape(styles().stateContent, true), false), escape(createComponent(JsonTree, { get value() { | ||
| return entry.instance.store?.state; | ||
| } })), ssrAttribute("class", escape(styles().detailSection, true), false), ssrAttribute("class", escape(styles().detailSectionHeader, true), false), ssrAttribute("class", escape(styles().stateContent, true), false), escape(createComponent(JsonTree, { get value() { | ||
| return entry.instance.options; | ||
| } })))]); | ||
| })()); | ||
| } | ||
| //#endregion | ||
| //#region src/components/Shell.tsx | ||
| var _tmpl$ = [ | ||
| "<div", | ||
| "><div", | ||
| " style=\"", | ||
| "\">", | ||
| "</div><div class=\"", | ||
| "\"></div><div", | ||
| " style=\"", | ||
| "\"><div", | ||
| ">Details</div>", | ||
| "</div></div>" | ||
| ]; | ||
| function Shell() { | ||
| const styles = useStyles(); | ||
| const state = usePacerDevtoolsState(); | ||
| const utilState = () => state; | ||
| const [selectedKey, setSelectedKey] = createSignal(null); | ||
| const [leftPanelWidth, setLeftPanelWidth] = createSignal(300); | ||
| const [isDragging, setIsDragging] = createSignal(false); | ||
| const selectedInstance = createMemo(() => { | ||
| const key = selectedKey(); | ||
| if (!key) return null; | ||
| for (const group of UTIL_GROUPS) { | ||
| const instance = utilState()[group.key].find((inst) => inst.key === key); | ||
| if (instance) return { | ||
| instance, | ||
| type: group.displayName | ||
| }; | ||
| } | ||
| return null; | ||
| }); | ||
| let dragStartX = 0; | ||
| let dragStartWidth = 0; | ||
| const handleMouseMove = (e) => { | ||
| if (!isDragging()) return; | ||
| e.preventDefault(); | ||
| const deltaX = e.clientX - dragStartX; | ||
| setLeftPanelWidth(Math.max(150, Math.min(800, dragStartWidth + deltaX))); | ||
| }; | ||
| const handleMouseUp = () => { | ||
| setIsDragging(false); | ||
| document.body.style.cursor = ""; | ||
| document.body.style.userSelect = ""; | ||
| }; | ||
| onMount(() => { | ||
| document.addEventListener("mousemove", handleMouseMove); | ||
| document.addEventListener("mouseup", handleMouseUp); | ||
| }); | ||
| onCleanup(() => { | ||
| document.removeEventListener("mousemove", handleMouseMove); | ||
| document.removeEventListener("mouseup", handleMouseUp); | ||
| }); | ||
| return createComponent(MainPanel, { get children() { | ||
| return [createComponent(Header, { get children() { | ||
| return createComponent(HeaderLogo, { | ||
| flavor: { | ||
| light: "#84cc16", | ||
| dark: "#84cc16" | ||
| }, | ||
| children: "TanStack Pacer" | ||
| }); | ||
| } }), ssr(_tmpl$, ssrAttribute("class", escape(styles().mainContainer, true), false), ssrAttribute("class", escape(styles().leftPanel, true), false), ssrStyleProperty("width:", `${escape(leftPanelWidth(), true)}px`) + ssrStyleProperty(";min-width:", "150px") + ssrStyleProperty(";max-width:", "800px"), escape(createComponent(UtilList, { | ||
| selectedKey, | ||
| setSelectedKey, | ||
| utilState | ||
| })), `${escape(styles().dragHandle, true)} ${isDragging() ? "dragging" : ""}`, ssrAttribute("class", escape(styles().rightPanel, true), false), ssrStyleProperty("flex:", 1), ssrAttribute("class", escape(styles().panelHeader, true), false), escape(createComponent(DetailsPanel, { | ||
| selectedInstance, | ||
| utilState | ||
| })))]; | ||
| } }); | ||
| } | ||
| //#endregion | ||
| //#region src/PacerDevtools.tsx | ||
| function PacerDevtools(props) { | ||
| return createComponent(ThemeContextProvider, { | ||
| get theme() { | ||
| return props.theme; | ||
| }, | ||
| get children() { | ||
| return createComponent(PacerContextProvider, { get children() { | ||
| return createComponent(Shell, {}); | ||
| } }); | ||
| } | ||
| }); | ||
| } | ||
| //#endregion | ||
| export { PacerDevtools as default }; |
| import { ThemeContextProvider } from '@tanstack/devtools-ui' | ||
| import { PacerContextProvider } from './PacerContextProvider' | ||
| import { Shell } from './components/Shell' | ||
| import type { TanStackDevtoolsTheme } from '@tanstack/devtools-ui' | ||
| interface PacerDevtools { | ||
| theme: TanStackDevtoolsTheme | ||
| } | ||
| export default function PacerDevtools(props: PacerDevtools) { | ||
| return ( | ||
| <ThemeContextProvider theme={props.theme}> | ||
| <PacerContextProvider> | ||
| <Shell /> | ||
| </PacerContextProvider> | ||
| </ThemeContextProvider> | ||
| ) | ||
| } |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
131710
-0.01%